Source Code Cross Referenced for GenericDatabase.java in  » Database-ORM » beankeeper » hu » netmind » persistence » 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 » beankeeper » hu.netmind.persistence 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * Copyright (C) 2006 NetMind Consulting Bt.
0003:         *
0004:         * This library is free software; you can redistribute it and/or
0005:         * modify it under the terms of the GNU Lesser General Public
0006:         * License as published by the Free Software Foundation; either
0007:         * version 3 of the License, or (at your option) any later version.
0008:         *
0009:         * This library is distributed in the hope that it will be useful,
0010:         * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:         * Lesser General Public License for more details.
0013:         *
0014:         * You should have received a copy of the GNU Lesser General Public
0015:         * License along with this library; if not, write to the Free Software
0016:         * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:         */package hu.netmind.persistence;
0018:
0019:        import java.util.Map;
0020:        import java.sql.Connection;
0021:        import java.sql.PreparedStatement;
0022:        import java.sql.SQLException;
0023:        import java.util.Iterator;
0024:        import java.util.HashSet;
0025:        import java.util.Set;
0026:        import java.util.Map;
0027:        import java.util.Vector;
0028:        import java.util.Date;
0029:        import java.util.Collections;
0030:        import java.util.Collection;
0031:        import java.util.List;
0032:        import java.util.HashMap;
0033:        import java.util.Stack;
0034:        import java.util.LinkedList;
0035:        import java.sql.Timestamp;
0036:        import org.apache.log4j.Logger;
0037:        import java.sql.ResultSet;
0038:        import java.sql.DatabaseMetaData;
0039:        import java.sql.Types;
0040:        import java.sql.ResultSetMetaData;
0041:        import hu.netmind.persistence.parser.Expression;
0042:        import hu.netmind.persistence.parser.ConstantTerm;
0043:        import hu.netmind.persistence.parser.ReferenceTerm;
0044:        import hu.netmind.persistence.parser.TableTerm;
0045:        import hu.netmind.persistence.parser.OrderBy;
0046:        import hu.netmind.persistence.parser.QueryStatement;
0047:
0048:        /**
0049:         * This is a generic database implementation. It contains no optimization,
0050:         * and tries to be as generic with types and sql syntax as possible, this also
0051:         * means, it cannot handle limits and offsets, which causes <strong>all</strong>
0052:         * results to be returned, even with lazy lists.
0053:         * @author Brautigam Robert
0054:         * @version Revision: $Revision$
0055:         */
0056:        public class GenericDatabase implements  DatabaseImplementation {
0057:            private static Logger logger = Logger
0058:                    .getLogger(GenericDatabase.class);
0059:            private static Logger sqlLogger = Logger
0060:                    .getLogger("hu.netmind.persistence.sql");
0061:            private static PerformanceLogger perfLogger = PerformanceLogger
0062:                    .getLogger();
0063:
0064:            /**
0065:             * Save basic attribute information, so we have it ready when needed.
0066:             */
0067:            private Map tableAttributes = Collections
0068:                    .synchronizedMap(new HashMap());
0069:
0070:            public GenericDatabase() {
0071:            }
0072:
0073:            protected Map getAttributes(String tableName) {
0074:                return (Map) tableAttributes.get(tableName.toLowerCase());
0075:            }
0076:
0077:            protected Class getAttributeType(String tableName,
0078:                    String attributeName) {
0079:                return (Class) getAttributes(tableName).get(
0080:                        attributeName.toLowerCase());
0081:            }
0082:
0083:            private void setAttributes(String tableName, Map attributes) {
0084:                tableAttributes.put(tableName, attributes);
0085:            }
0086:
0087:            /**
0088:             * Make a save statement for given table, id and changed attributes.
0089:             * Override this method in a subclass for a non-generic behaviour.
0090:             * The attributes' placeholders must be in the same order as given
0091:             * by the attributeNames list.
0092:             * @param tableName The table to save attributes to.
0093:             * @param keyNames The keys of object to save (All object entries have keys).
0094:             * @param attributeNames The attributes that will change.
0095:             * the statement.
0096:             * @param keys Keys.
0097:             * @return An SQL save/update statement specific to database backend.
0098:             */
0099:            protected String getSaveStatement(String tableName, List keyNames,
0100:                    List attributeNames, Map keys) {
0101:                // Make a generic update statement
0102:                StringBuffer statement = new StringBuffer("update " + tableName
0103:                        + " set ");
0104:                for (int i = 0; i < attributeNames.size(); i++)
0105:                    statement.append(attributeNames.get(i).toString() + "=?,");
0106:                statement.delete(statement.length() - 1, statement.length());
0107:                if (keyNames.size() > 0)
0108:                    statement.append(" where ");
0109:                for (int i = 0; i < keyNames.size(); i++) {
0110:                    String keyName = keyNames.get(i).toString();
0111:                    if (keys.get(keyName) == null) {
0112:                        statement.append(keyName + " is null and ");
0113:                        keyNames.remove(keyName);
0114:                        i--;
0115:                    } else
0116:                        statement.append(keyName + "=? and ");
0117:                }
0118:                statement.delete(statement.length() - 5, statement.length());
0119:                sqlLogger.debug("preparing update statement: "
0120:                        + statement.toString());
0121:                // Return 
0122:                return statement.toString();
0123:            }
0124:
0125:            /**
0126:             * Modifies an object already in database with given fields.
0127:             * @param tableName The table to save attributes to.
0128:             * @param id The id of object to save (All object entries have an id).
0129:             * @param attributes The attributes in form of name:value pairs.
0130:             */
0131:            public DatabaseStatistics save(Connection connection,
0132:                    String tableName, Map keys, Map attributes) {
0133:                DatabaseStatistics stats = new DatabaseStatistics();
0134:                // Clear null values from attributes
0135:                attributes = new HashMap(attributes);
0136:                Vector nullVector = new Vector();
0137:                nullVector.add(null);
0138:                attributes.values().removeAll(nullVector);
0139:                // Make a generic insert statement
0140:                Vector attributeNames = new Vector(attributes.keySet());
0141:                Vector keyNames = new Vector(keys.keySet());
0142:                String statement = getSaveStatement(tableName, keyNames,
0143:                        attributeNames, keys);
0144:                // Prepare
0145:                PreparedStatement pstmt;
0146:                try {
0147:                    pstmt = connection.prepareStatement(statement);
0148:                    int i = 0;
0149:                    for (; i < attributeNames.size(); i++) {
0150:                        Object value = getSQLValue(attributes
0151:                                .get(attributeNames.get(i)));
0152:                        sqlLogger.debug("setting statement parameter #" + i
0153:                                + ": " + value);
0154:                        pstmt.setObject(i + 1, value);
0155:                    }
0156:                    for (; i < keyNames.size() + attributeNames.size(); i++) {
0157:                        Object value = getSQLValue(keys.get(keyNames.get(i
0158:                                - attributeNames.size())));
0159:                        sqlLogger.debug("setting statement parameter #" + i
0160:                                + ": " + value);
0161:                        pstmt.setObject(i + 1, value);
0162:                    }
0163:                } catch (Exception e) {
0164:                    throw new StoreException("cannot prepare statement: "
0165:                            + statement, e);
0166:                }
0167:                // Execute
0168:                try {
0169:                    if (logger.isDebugEnabled())
0170:                        logger.debug("excuting update statement: " + statement);
0171:                    long startTime = System.currentTimeMillis();
0172:                    pstmt.executeUpdate();
0173:                    long endTime = System.currentTimeMillis();
0174:                    perfLogger.log("Update statement execution",
0175:                            new int[] { (int) (endTime - startTime) });
0176:                    stats.setUpdateCount(1);
0177:                    stats.setUpdateTime(endTime - startTime);
0178:                } catch (Exception e) {
0179:                    throw new StoreException("exception while sql update", e);
0180:                } finally {
0181:                    try {
0182:                        pstmt.close();
0183:                    } catch (Exception e) {
0184:                        logger.debug("unable to close statement", e);
0185:                    }
0186:                }
0187:                // Return
0188:                return stats;
0189:            }
0190:
0191:            /**
0192:             * Make an insert statement for given table, id and attributes.
0193:             * Override this method in a subclass for a non-generic behaviour.
0194:             * The attributes' placeholders must be in the same order as given
0195:             * by the attributeNames list.
0196:             * @param tableName The table to insert attributes to.
0197:             * @param attributeNames The attributes that will be inserted.
0198:             * @return An SQL insert statement specific to database backend.
0199:             */
0200:            protected String getInsertStatement(String tableName,
0201:                    List attributeNames) {
0202:                // Make a generic insert statement
0203:                StringBuffer statement = new StringBuffer("insert into "
0204:                        + tableName + " (");
0205:                for (int i = 0; i < attributeNames.size(); i++)
0206:                    statement.append(attributeNames.get(i).toString() + ",");
0207:                statement.delete(statement.length() - 1, statement.length());
0208:                statement.append(") values (");
0209:                for (int i = 0; i < attributeNames.size(); i++)
0210:                    statement.append("?,");
0211:                statement.delete(statement.length() - 1, statement.length());
0212:                statement.append(")");
0213:                sqlLogger.debug("preparing insert statement: "
0214:                        + statement.toString());
0215:                // Return
0216:                return statement.toString();
0217:            }
0218:
0219:            /**
0220:             * Insert an object into the database.
0221:             * @param tableName The table to save attributes to.
0222:             * @param id The id of object to save (All object entries have an id).
0223:             * @param attributes The attributes in form of name:value pairs.
0224:             */
0225:            public DatabaseStatistics insert(Connection connection,
0226:                    String tableName, Map attributes) {
0227:                DatabaseStatistics stats = new DatabaseStatistics();
0228:                // Clear null values from attributes
0229:                attributes = new HashMap(attributes);
0230:                Vector nullVector = new Vector();
0231:                nullVector.add(null);
0232:                attributes.values().removeAll(nullVector);
0233:                // Make a generic insert statement
0234:                Vector attributeNames = new Vector(attributes.keySet());
0235:                String statement = getInsertStatement(tableName, attributeNames);
0236:                // Execute
0237:                PreparedStatement pstmt;
0238:                try {
0239:                    pstmt = connection.prepareStatement(statement);
0240:                    for (int i = 0; i < attributeNames.size(); i++) {
0241:                        Object value = getSQLValue(attributes
0242:                                .get(attributeNames.get(i)));
0243:                        sqlLogger.debug("setting statement parameter #" + i
0244:                                + ": " + value);
0245:                        pstmt.setObject(i + 1, value);
0246:                    }
0247:                } catch (Exception e) {
0248:                    throw new StoreException("cannot prepare statement: "
0249:                            + statement, e);
0250:                }
0251:                // Execute
0252:                try {
0253:                    if (logger.isDebugEnabled())
0254:                        logger.debug("excuting insert statement: " + statement);
0255:                    long startTime = System.currentTimeMillis();
0256:                    pstmt.executeUpdate();
0257:                    long endTime = System.currentTimeMillis();
0258:                    perfLogger.log("Insert statement execution",
0259:                            new int[] { (int) (endTime - startTime) });
0260:                    stats.setInsertCount(1);
0261:                    stats.setInsertTime(endTime - startTime);
0262:                } catch (Exception e) {
0263:                    throw new StoreException("exception while sql insert", e);
0264:                } finally {
0265:                    try {
0266:                        pstmt.close();
0267:                    } catch (Exception e) {
0268:                        logger.debug("unable to close statement", e);
0269:                    }
0270:                }
0271:                // Return
0272:                return stats;
0273:            }
0274:
0275:            /**
0276:             * Make a remove statement for given table and attributes.
0277:             * Override this method in a subclass for a non-generic behaviour.
0278:             * The attributes' placeholders must be in the same order as given
0279:             * by the attributeNames list.
0280:             * @param tableName The table to remove attributes from.
0281:             * @param attributeNames The attributes that will be search for by the remove.
0282:             * @return An SQL insert statement specific to database backend.
0283:             */
0284:            protected String getRemoveStatement(String tableName,
0285:                    List attributeNames) {
0286:                // Make a generic delete statement
0287:                // Please note the paticularly funny delete-6 to handle
0288:                // empty lists and list ends.
0289:                StringBuffer statement = new StringBuffer("delete from "
0290:                        + tableName + " where ");
0291:                for (int i = 0; i < attributeNames.size(); i++)
0292:                    statement.append(attributeNames.get(i).toString()
0293:                            + "=?  and  ");
0294:                statement.delete(statement.length() - 6, statement.length());
0295:                sqlLogger.debug("preparing delete statement: "
0296:                        + statement.toString());
0297:                // Return
0298:                return statement.toString();
0299:            }
0300:
0301:            /**
0302:             * Remove an entry from database.
0303:             * @param tableName The table to remove object from.
0304:             * @param attributes The attributes which identify the object.
0305:             * Equality is assumed with each attribute and it's value.
0306:             */
0307:            public DatabaseStatistics remove(Connection connection,
0308:                    String tableName, Map attributes) {
0309:                DatabaseStatistics stats = new DatabaseStatistics();
0310:                // Clear null values from attributes
0311:                attributes = new HashMap(attributes);
0312:                Vector nullVector = new Vector();
0313:                nullVector.add(null);
0314:                attributes.values().removeAll(nullVector);
0315:                // Make statement
0316:                Vector attributeNames = new Vector(attributes.keySet());
0317:                String statement = getRemoveStatement(tableName, attributeNames);
0318:                // Prepare
0319:                PreparedStatement pstmt;
0320:                try {
0321:                    pstmt = connection.prepareStatement(statement);
0322:                    for (int i = 0; i < attributeNames.size(); i++)
0323:                        pstmt.setObject(i + 1, getSQLValue(attributes
0324:                                .get(attributeNames.get(i))));
0325:                } catch (Exception e) {
0326:                    throw new StoreException("cannot prepare statement: "
0327:                            + statement, e);
0328:                }
0329:                // Execute
0330:                try {
0331:                    long startTime = System.currentTimeMillis();
0332:                    pstmt.executeUpdate();
0333:                    long endTime = System.currentTimeMillis();
0334:                    perfLogger.log("Remove statement execution",
0335:                            new int[] { (int) (endTime - startTime) });
0336:                    stats.setDeleteCount(1);
0337:                    stats.setDeleteTime(endTime - startTime);
0338:                } catch (Exception e) {
0339:                    throw new StoreException("exception while sql delete", e);
0340:                } finally {
0341:                    try {
0342:                        pstmt.close();
0343:                    } catch (Exception e) {
0344:                        logger.debug("unable to close statement", e);
0345:                    }
0346:                }
0347:                // Return
0348:                return stats;
0349:            }
0350:
0351:            /**
0352:             * Drop the table with given name.
0353:             * This method is not called directly, but from <code>ensureTable</code>.
0354:             * @param tableName The table to drop.
0355:             */
0356:            protected DatabaseStatistics dropTable(Connection connection,
0357:                    String tableName) {
0358:                DatabaseStatistics stats = new DatabaseStatistics();
0359:                // Create statement
0360:                String statement = "drop table " + tableName;
0361:                // Execute statement
0362:                long startTime = System.currentTimeMillis();
0363:                executeUpdate(connection, statement);
0364:                long endTime = System.currentTimeMillis();
0365:                stats.setSchemaCount(1);
0366:                stats.setSchemaTime(endTime - startTime);
0367:                return stats;
0368:            }
0369:
0370:            /**
0371:             * Get the create table statement before the attributes part.
0372:             */
0373:            protected String getCreateTableStatement(Connection connection,
0374:                    String tableName) {
0375:                return "create table " + tableName;
0376:            }
0377:
0378:            /**
0379:             * Create table with given name, attribute types, and keys.
0380:             * This method is not called directly, but from <code>ensureTable</code>.
0381:             * @param tableName The table to create.
0382:             * @param attributeTypes The attribute names together with which
0383:             * java class they should hold.
0384:             */
0385:            protected DatabaseStatistics createTable(Connection connection,
0386:                    String tableName, Map attributeTypes, List keyAttributeNames) {
0387:                DatabaseStatistics stats = new DatabaseStatistics();
0388:                // Create statement
0389:                StringBuffer statement = new StringBuffer(
0390:                        getCreateTableStatement(connection, tableName) + " (");
0391:                Iterator iterator = attributeTypes.entrySet().iterator();
0392:                while (iterator.hasNext()) {
0393:                    Map.Entry entry = (Map.Entry) iterator.next();
0394:                    statement.append(entry.getKey().toString()
0395:                            + " "
0396:                            + getSQLTypeName(getSQLType(((Class) entry
0397:                                    .getValue()))) + ",");
0398:                }
0399:                if ((keyAttributeNames != null)
0400:                        && (keyAttributeNames.size() > 0)) {
0401:                    statement.append(" primary key (");
0402:                    for (int i = 0; i < keyAttributeNames.size(); i++)
0403:                        statement.append(keyAttributeNames.get(i) + ",");
0404:                    statement
0405:                            .delete(statement.length() - 1, statement.length());
0406:                    statement.append(")  ");
0407:                }
0408:                statement.delete(statement.length() - 1, statement.length());
0409:                statement.append(")");
0410:                // Execute statement
0411:                long startTime = System.currentTimeMillis();
0412:                executeUpdate(connection, statement.toString());
0413:                long endTime = System.currentTimeMillis();
0414:                stats.setSchemaCount(1);
0415:                stats.setSchemaTime(endTime - startTime);
0416:                // Create initial indexes (currently all attributes will be indexed)
0417:                // Do not create for reserved tables
0418:                if ((!tableName.equalsIgnoreCase("ids"))
0419:                        && (!tableName.equalsIgnoreCase("tablemap"))
0420:                        && (!tableName.equalsIgnoreCase("nodes"))
0421:                        && (!tableName.equalsIgnoreCase("classes")))
0422:                    stats.add(createIndexes(connection, tableName,
0423:                            attributeTypes));
0424:                return stats;
0425:            }
0426:
0427:            /**
0428:             * Get the statement to drop a column.
0429:             */
0430:            protected String getDropColumnStatement(String tableName,
0431:                    String columnName) {
0432:                return "alter table " + tableName + " drop column "
0433:                        + columnName;
0434:            }
0435:
0436:            /**
0437:             * Get the statement to add a column to a table.
0438:             */
0439:            protected String getAddColumnStatement(String tableName,
0440:                    String columnName, String columnType) {
0441:                return "alter table " + tableName + " add column " + columnName
0442:                        + " " + columnType;
0443:            }
0444:
0445:            /**
0446:             * Drop a column from a table.
0447:             * @param connection The connection object.
0448:             * @param tableName The table to drop column from.
0449:             * @param columnName The column to drop.
0450:             */
0451:            protected DatabaseStatistics dropColumn(Connection connection,
0452:                    String tableName, String columnName) {
0453:                DatabaseStatistics stats = new DatabaseStatistics();
0454:                long startTime = System.currentTimeMillis();
0455:                executeUpdate(connection, getDropColumnStatement(tableName,
0456:                        columnName));
0457:                long endTime = System.currentTimeMillis();
0458:                stats.setSchemaCount(1);
0459:                stats.setSchemaTime(endTime - startTime);
0460:                return stats;
0461:            }
0462:
0463:            /**
0464:             * Add a column to a table.
0465:             * @param connection The connection object.
0466:             * @param tableName The table to drop column from.
0467:             * @param columnName The column to create.
0468:             * @param columnType The column type to create.
0469:             */
0470:            protected DatabaseStatistics addColumn(Connection connection,
0471:                    String tableName, String columnName, String columnType) {
0472:                DatabaseStatistics stats = new DatabaseStatistics();
0473:                long startTime = System.currentTimeMillis();
0474:                executeUpdate(connection, getAddColumnStatement(tableName,
0475:                        columnName, columnType));
0476:                long endTime = System.currentTimeMillis();
0477:                stats.setSchemaCount(1);
0478:                stats.setSchemaTime(endTime - startTime);
0479:                return stats;
0480:            }
0481:
0482:            /**
0483:             * Alter the table. Implementation: Not all databases support
0484:             * altering multiple columns, so all columns are separately modified.
0485:             * @param connection The connection object.
0486:             * @param tableName The table name.
0487:             * @param removedAttributes Attribute names which should be removed.
0488:             * @param addedAttributes Attribute names which should be added.
0489:             * @param attributeTypes Types in form of Classes to given names.
0490:             */
0491:            protected DatabaseStatistics alterTable(Connection connection,
0492:                    String tableName, List removedAttributes,
0493:                    List addedAttributes, Map attributeTypes,
0494:                    List keyAttributeNames) {
0495:                DatabaseStatistics stats = new DatabaseStatistics();
0496:                if ((removedAttributes.size() == 0)
0497:                        && (addedAttributes.size() == 0)) {
0498:                    logger
0499:                            .debug("table '"
0500:                                    + tableName
0501:                                    + "' schema matches class, no modification required.");
0502:                    return stats;
0503:                }
0504:                logger.debug("table layout mismatch for table '" + tableName
0505:                        + "': removed columns: " + removedAttributes
0506:                        + ", added columns: " + addedAttributes);
0507:                // Assemble statements and execute them
0508:                for (int i = 0; i < removedAttributes.size(); i++) {
0509:                    String attributeName = (String) removedAttributes.get(i);
0510:                    stats.add(dropColumn(connection, tableName, attributeName));
0511:                }
0512:                for (int i = 0; i < addedAttributes.size(); i++) {
0513:                    String attributeName = (String) addedAttributes.get(i);
0514:                    String sqlTypeName = getSQLTypeName(getSQLType((Class) attributeTypes
0515:                            .get(addedAttributes.get(i))));
0516:                    stats.add(addColumn(connection, tableName, attributeName,
0517:                            sqlTypeName));
0518:                }
0519:                // Create initial indexes (currently all new attributes will be indexed)
0520:                HashMap addedTypes = new HashMap();
0521:                for (int i = 0; i < addedAttributes.size(); i++) {
0522:                    Object name = addedAttributes.get(i);
0523:                    addedTypes.put(name, attributeTypes.get(name));
0524:                }
0525:                stats.add(createIndexes(connection, tableName, addedTypes));
0526:                return stats;
0527:            }
0528:
0529:            /**
0530:             * Get index creation statement for a given table and field.
0531:             * @return The statement to use, or null, of no such index can be
0532:             * created.
0533:             */
0534:            protected String getCreateIndexStatement(String indexName,
0535:                    String tableName, String field, Class fieldClass) {
0536:                if (fieldClass.equals(byte[].class))
0537:                    return null;
0538:                return "create index " + indexName + " on " + tableName + " ("
0539:                        + field + ")";
0540:            }
0541:
0542:            /**
0543:             * Get an unused index name.
0544:             */
0545:            protected String getCreateIndexName(Connection connection,
0546:                    String tableName, String field) {
0547:                try {
0548:                    // Get max length
0549:                    DatabaseMetaData dmd = connection.getMetaData();
0550:                    int maxTableNameLength = dmd.getMaxTableNameLength();
0551:                    if (maxTableNameLength == 0)
0552:                        maxTableNameLength = Integer.MAX_VALUE;
0553:                    // Check whether trivial name is good enough
0554:                    String result = tableName + "_idx_" + field;
0555:                    if (result.length() <= maxTableNameLength)
0556:                        return result;
0557:                    // Assemble all indexes to table
0558:                    HashSet indexNames = new HashSet();
0559:                    ResultSet rs = dmd.getIndexInfo(null, null, tableName,
0560:                            false, true);
0561:                    while (rs.next()) {
0562:                        String indexName = rs.getString("INDEX_NAME");
0563:                        if (indexName != null)
0564:                            indexNames.add(indexName.toLowerCase());
0565:                    }
0566:                    rs.close();
0567:                    // Create an index name which does not currently exist
0568:                    if (logger.isDebugEnabled())
0569:                        logger.debug("creating index name, existing indexes: "
0570:                                + indexNames);
0571:                    result = result.substring(0, maxTableNameLength - 4)
0572:                            .toLowerCase();
0573:                    int nameIndex = 1;
0574:                    while (indexNames.contains(result + nameIndex))
0575:                        nameIndex++;
0576:                    // Return abbr. name
0577:                    logger.debug("new index name: " + result + nameIndex);
0578:                    return result + nameIndex;
0579:                } catch (SQLException e) {
0580:                    throw new StoreException(
0581:                            "Could not compute approriate index name.", e);
0582:                }
0583:            }
0584:
0585:            /**
0586:             * Create indexes to the given attributes.
0587:             * @param connection The SQL connection.
0588:             * @param tableName The table to create indexes to.
0589:             * @param attributeTypes The attributes and their types to create indexes to.
0590:             */
0591:            protected DatabaseStatistics createIndexes(Connection connection,
0592:                    String tableName, Map attributeTypes) {
0593:                DatabaseStatistics stats = new DatabaseStatistics();
0594:                Iterator entries = attributeTypes.entrySet().iterator();
0595:                while (entries.hasNext()) {
0596:                    Map.Entry entry = (Map.Entry) entries.next();
0597:                    String indexName = getCreateIndexName(connection,
0598:                            tableName, entry.getKey().toString());
0599:                    String statement = getCreateIndexStatement(indexName,
0600:                            tableName, entry.getKey().toString(), (Class) entry
0601:                                    .getValue());
0602:                    if (statement != null) {
0603:                        long startTime = System.currentTimeMillis();
0604:                        executeUpdate(connection, statement);
0605:                        long endTime = System.currentTimeMillis();
0606:                        stats.setSchemaCount(stats.getSchemaCount() + 1);
0607:                        stats.setSchemaTime(stats.getSchemaTime()
0608:                                + (endTime - startTime));
0609:                    }
0610:                }
0611:                return stats;
0612:            }
0613:
0614:            /**
0615:             * Get the data types of a given table.
0616:             * @return A map of names with the sql type number as value.
0617:             */
0618:            protected HashMap getTableAttributeTypes(Connection connection,
0619:                    String tableName) throws SQLException {
0620:                DatabaseMetaData dmd = connection.getMetaData();
0621:                ResultSet rs = dmd.getColumns(null, null, tableName, null);
0622:                HashMap databaseAttributeTypes = new HashMap();
0623:                while (rs.next()) {
0624:                    // The tablename was a wildcard, so make sure that we
0625:                    // get the right table's column (thanks Daniel)
0626:                    if (rs.getString("TABLE_NAME").equalsIgnoreCase(tableName)) {
0627:                        int type = getTableAttributeType(rs);
0628:                        databaseAttributeTypes.put(rs.getObject("COLUMN_NAME")
0629:                                .toString().toLowerCase(), new Integer(type));
0630:                    }
0631:                }
0632:                rs.close();
0633:                return databaseAttributeTypes;
0634:            }
0635:
0636:            /**
0637:             * Override this method to get different types for attributes than
0638:             * the database reports.
0639:             */
0640:            protected int getTableAttributeType(ResultSet rs)
0641:                    throws SQLException {
0642:                return rs.getInt("DATA_TYPE");
0643:            }
0644:
0645:            /**
0646:             * Ensure that table exists in database. Implementation does
0647:             * not check keys. If keys differ, this implementation will not correct
0648:             * the problem. Column renames are not detected, if a column is renamed,
0649:             * the old column will be dropped and a new column will be created.
0650:             * @param tableName The table to check.
0651:             * @param attributeTypes The attribute names together with which
0652:             * java class they should hold.
0653:             * @param keyAttributeNames The keys of table.
0654:             */
0655:            public DatabaseStatistics ensureTable(Connection connection,
0656:                    String tableName, Map attributeTypes,
0657:                    List keyAttributeNames, boolean create) {
0658:                DatabaseStatistics stats = new DatabaseStatistics();
0659:                try {
0660:                    if (create) {
0661:                        // Query whether table exists
0662:                        HashMap databaseAttributeTypes = getTableAttributeTypes(
0663:                                connection, tableName);
0664:                        // Diff the database schema and attribute schema
0665:                        logger
0666:                                .debug("comparing database schema with class, database has: "
0667:                                        + databaseAttributeTypes
0668:                                        + ", class has: " + attributeTypes);
0669:                        if (databaseAttributeTypes.size() == 0) {
0670:                            // No columns found, better create that table
0671:                            logger.debug("table '" + tableName
0672:                                    + "' did not exist, creating.");
0673:                            stats.add(createTable(connection, tableName,
0674:                                    attributeTypes, keyAttributeNames));
0675:                        } else {
0676:                            // Columns found, so make diff
0677:                            // Note, that not only removed and added columns count, but
0678:                            // also columns which types have changed, since that also
0679:                            // means deleting and adding the column
0680:                            Vector removedAttributes = new Vector(
0681:                                    databaseAttributeTypes.keySet());
0682:                            removedAttributes
0683:                                    .removeAll(attributeTypes.keySet());
0684:                            Vector addedAttributes = new Vector(attributeTypes
0685:                                    .keySet());
0686:                            addedAttributes.removeAll(databaseAttributeTypes
0687:                                    .keySet());
0688:                            Vector changedAttributes = new Vector(
0689:                                    databaseAttributeTypes.keySet());
0690:                            changedAttributes
0691:                                    .retainAll(attributeTypes.keySet());
0692:                            Iterator changedAttributesIterator = changedAttributes
0693:                                    .iterator();
0694:                            while (changedAttributesIterator.hasNext()) {
0695:                                String attribute = (String) changedAttributesIterator
0696:                                        .next().toString();
0697:                                if (getSQLTypeName(
0698:                                        ((Integer) databaseAttributeTypes
0699:                                                .get(attribute)).intValue())
0700:                                        .equals(
0701:                                                getSQLTypeName(getSQLType((Class) attributeTypes
0702:                                                        .get(attribute)))))
0703:                                    changedAttributesIterator.remove(); // Remove if type matches
0704:                            }
0705:                            removedAttributes.addAll(changedAttributes);
0706:                            addedAttributes.addAll(changedAttributes);
0707:                            // If all columns are to be removed, drop the table and re-create
0708:                            // it, or else first drop the old columns and add the new ones
0709:                            databaseAttributeTypes.remove("persistence_id");
0710:                            databaseAttributeTypes.remove("persistence_start");
0711:                            databaseAttributeTypes.remove("persistence_end");
0712:                            databaseAttributeTypes.remove("persistence_txend");
0713:                            databaseAttributeTypes
0714:                                    .remove("persistence_txstart");
0715:                            databaseAttributeTypes
0716:                                    .remove("persistence_txstartid");
0717:                            databaseAttributeTypes
0718:                                    .remove("persistence_txendid");
0719:                            if ((removedAttributes.size() == databaseAttributeTypes
0720:                                    .size())
0721:                                    && (removedAttributes.size() > 0)) {
0722:                                // Uh-oh, all attributes changed, re-create table
0723:                                logger
0724:                                        .debug("table '"
0725:                                                + tableName
0726:                                                + "' will be re-created, because all attributes changed apparently, removed: "
0727:                                                + removedAttributes
0728:                                                + ", added: " + addedAttributes);
0729:                                stats.add(dropTable(connection, tableName));
0730:                                stats.add(createTable(connection, tableName,
0731:                                        attributeTypes, keyAttributeNames));
0732:                            } else if ((removedAttributes.size() > 0)
0733:                                    || (addedAttributes.size() > 0)) {
0734:                                // Partial change, only change what is necessary
0735:                                logger.debug("table '" + tableName
0736:                                        + "' has a partial change, removing: "
0737:                                        + removedAttributes + ", adding: "
0738:                                        + addedAttributes);
0739:                                stats.add(alterTable(connection, tableName,
0740:                                        removedAttributes, addedAttributes,
0741:                                        attributeTypes, keyAttributeNames));
0742:                            } else {
0743:                                logger.debug("nothing to change on table: "
0744:                                        + tableName);
0745:                            }
0746:                        }
0747:                    } else {
0748:                        logger
0749:                                .debug("table is not to be created: "
0750:                                        + tableName);
0751:                    }
0752:                    // Remember these attributes
0753:                    setAttributes(tableName, attributeTypes);
0754:                } catch (Exception e) {
0755:                    throw new StoreException("could not ensure table exists: "
0756:                            + tableName, e);
0757:                }
0758:                // Return
0759:                return stats;
0760:            }
0761:
0762:            /**
0763:             * Get the limit component of statement, if it can be expressed in
0764:             * the current database with simple statement part.
0765:             * @param limits The limits to apply.
0766:             */
0767:            protected String getLimitStatement(String statement, Limits limits,
0768:                    List types) {
0769:                return statement;
0770:            }
0771:
0772:            /**
0773:             * Get the count statement for the given statement.
0774:             */
0775:            protected String getCountStatement(String stmt) {
0776:                return "select count(*) from (" + stmt + ") as cr";
0777:            }
0778:
0779:            /**
0780:             * Get the table declaration for a select statment.
0781:             */
0782:            protected String getTableDeclaration(String tableName, String alias) {
0783:                if (alias == null)
0784:                    return tableName;
0785:                else
0786:                    return tableName + " as " + alias;
0787:            }
0788:
0789:            /**
0790:             * Assemble the query columns of a given term.
0791:             */
0792:            protected String getQuerySource(TableTerm term, Set queryColumns,
0793:                    List types) {
0794:                // Determine name
0795:                String name = term.getName();
0796:                // List all attributes one-by-one
0797:                StringBuffer result = new StringBuffer();
0798:                Map attributeTypes = getAttributes(term.getTableName());
0799:                if (attributeTypes == null)
0800:                    throw new StoreException(
0801:                            "attributes types not present for table: "
0802:                                    + term.getTableName() + ", map: "
0803:                                    + tableAttributes);
0804:                Iterator attributeEntryIterator = attributeTypes.entrySet()
0805:                        .iterator();
0806:                while (attributeEntryIterator.hasNext()) {
0807:                    Map.Entry entry = (Map.Entry) attributeEntryIterator.next();
0808:                    String attributeName = (String) entry.getKey();
0809:                    Class attributeType = (Class) entry.getValue();
0810:                    if (!attributeName.startsWith("persistence_")) {
0811:                        result.append(name + "." + attributeName + ",");
0812:                        types.add(attributeType);
0813:                        checkAttribute(queryColumns, attributeName);
0814:                    }
0815:                }
0816:                if ((result.length() > 0)
0817:                        && (result.charAt(result.length() - 1) == ','))
0818:                    result.deleteCharAt(result.length() - 1);
0819:                return result.toString();
0820:            }
0821:
0822:            /**
0823:             * Check the attribute whether it's already contained in the
0824:             * columns.
0825:             */
0826:            private void checkAttribute(Set queryColumns, String attributeName) {
0827:                if (!queryColumns.add(attributeName))
0828:                    throw new StoreException(
0829:                            "query has multiple columns with same name, try to add: "
0830:                                    + attributeName
0831:                                    + ", attributes until now: " + queryColumns);
0832:            }
0833:
0834:            /**
0835:             * Assemble the query columns of select statement.
0836:             * @param stmt The statement to get query source from.
0837:             * @param types The type list to fill in. Each queried column's
0838:             * type should be inserted into this list in order.
0839:             * @return The columns part of the select statement.
0840:             */
0841:            protected String getQuerySource(QueryStatement stmt, List types) {
0842:                if (logger.isDebugEnabled())
0843:                    logger.debug("computing query source from: "
0844:                            + stmt.getSelectTerms());
0845:                HashSet queryColumns = new HashSet();
0846:                StringBuffer querySource = new StringBuffer();
0847:                for (int i = 0; i < stmt.getSelectTerms().size(); i++) {
0848:                    if (querySource.length() > 0)
0849:                        querySource.append(",");
0850:                    TableTerm term = (TableTerm) stmt.getSelectTerms().get(i);
0851:                    if (term instanceof  ReferenceTerm) {
0852:                        logger.debug("adding reference term: " + term);
0853:                        // This is specifically an attribute
0854:                        ReferenceTerm refTerm = (ReferenceTerm) term;
0855:                        querySource.append(refTerm.getName() + "."
0856:                                + refTerm.getColumnName());
0857:                        if (refTerm.getColumnAlias() != null) {
0858:                            querySource.append(" as "
0859:                                    + refTerm.getColumnAlias());
0860:                            checkAttribute(queryColumns, refTerm
0861:                                    .getColumnAlias());
0862:                        } else {
0863:                            checkAttribute(queryColumns, refTerm
0864:                                    .getColumnName());
0865:                        }
0866:                        Class attributeType = getAttributeType(term
0867:                                .getTableName(), ((ReferenceTerm) term)
0868:                                .getColumnName());
0869:                        types.add(attributeType);
0870:                    } else {
0871:                        if (logger.isDebugEnabled())
0872:                            logger.debug("adding table term: " + term
0873:                                    + ", left terms: "
0874:                                    + term.getLeftTableTerms());
0875:                        // This is a table
0876:                        querySource.append(getQuerySource(term, queryColumns,
0877:                                types));
0878:                        for (int o = 0; o < term.getLeftTableTerms().size(); o++) {
0879:                            TableTerm leftTableTerm = (TableTerm) term
0880:                                    .getLeftTableTerms().get(o);
0881:                            String sourcePart = getQuerySource(leftTableTerm,
0882:                                    queryColumns, types);
0883:                            if (sourcePart.length() > 0) {
0884:                                if (querySource.length() > 0)
0885:                                    querySource.append(",");
0886:                                querySource.append(sourcePart);
0887:                            }
0888:                        }
0889:                    }
0890:                }
0891:                // Add persistence id/startserial/endserial
0892:                TableTerm firstTerm = null;
0893:                if (stmt.getSelectTerms().size() > 0)
0894:                    firstTerm = (TableTerm) stmt.getSelectTerms().get(0);
0895:                if ((!"tablemap".equalsIgnoreCase(firstTerm.getTableName()))
0896:                        && (!"classes".equalsIgnoreCase(firstTerm
0897:                                .getTableName()))
0898:                        && (!"ids".equalsIgnoreCase(firstTerm.getTableName()))
0899:                        && (!"nodes".equalsIgnoreCase(firstTerm.getTableName()))
0900:                        && (stmt.getMode() != QueryStatement.MODE_VIEW)) {
0901:                    // Add persistence id
0902:                    if (querySource.length() > 0)
0903:                        querySource.append(",");
0904:                    querySource.append(firstTerm.getName() + ".persistence_id");
0905:                    types.add(Long.class);
0906:                    // Add serials
0907:                    Vector terms = new Vector();
0908:                    terms.add(firstTerm);
0909:                    terms.addAll(firstTerm.getLeftTableTerms());
0910:                    for (int i = 0; i < terms.size(); i++) {
0911:                        querySource.append(",");
0912:                        querySource
0913:                                .append(firstTerm.getName()
0914:                                        + ".persistence_start as persistence_start"
0915:                                        + i);
0916:                        types.add(Long.class);
0917:                        querySource.append(",");
0918:                        querySource.append(firstTerm.getName()
0919:                                + ".persistence_end as persistence_end" + i);
0920:                        types.add(Long.class);
0921:                    }
0922:                }
0923:                // Return
0924:                return querySource.toString();
0925:            }
0926:
0927:            /**
0928:             * Transform the expression. This method is called on each subsequent
0929:             * expressions too, so implementation does not have to be recursive.
0930:             * @param expr The expression to possibly transform.
0931:             * @return A transformed expression.
0932:             */
0933:            protected Expression transformExpression(Expression expr) {
0934:                return expr;
0935:            }
0936:
0937:            /**
0938:             * Checks whether a list of table terms contains a given reference term,
0939:             * but not by using the standard <code>equals()</code> method, but by
0940:             * comparing a reference term with own equality.
0941:             */
0942:            private boolean containsReferenceTerm(List terms, ReferenceTerm term) {
0943:                for (int i = 0; i < terms.size(); i++) {
0944:                    TableTerm objRaw = (TableTerm) terms.get(i);
0945:                    if (objRaw instanceof  ReferenceTerm) {
0946:                        ReferenceTerm obj = (ReferenceTerm) objRaw;
0947:                        if ((obj.equals(term))
0948:                                && (obj.getColumnName().equals(term
0949:                                        .getColumnName())))
0950:                            return true;
0951:                    }
0952:                }
0953:                return false;
0954:            }
0955:
0956:            /**
0957:             * Select objects from database as ordered list of attribute maps.
0958:             * @param connection The connection to run statements in.
0959:             * @param stmt The query statement.
0960:             * @param limits The limits of the result. (Offset, maximum result count)
0961:             * @param result The result object.
0962:             */
0963:            public DatabaseStatistics search(Connection connection,
0964:                    QueryStatement stmt, Limits limits,
0965:                    SearchResult searchResult) {
0966:                DatabaseStatistics stats = new DatabaseStatistics();
0967:                Expression expression = stmt.getQueryExpression();
0968:                List orderBys = stmt.getOrderByList();
0969:                // Assemble statement:
0970:                // - Parse conditions, determine all tables to join
0971:                // - Create statement without orderbys and limits 
0972:                // - Run count statement to determine full count
0973:                // - Apply limits and orderbys to statement
0974:                // - Run statement to get results
0975:
0976:                // Compute query columns and tables
0977:                Vector types = new Vector();
0978:                String querySource = getQuerySource(stmt, types);
0979:                if (logger.isDebugEnabled())
0980:                    logger.debug("query source: " + querySource
0981:                            + ", type vector: " + types + ", select terms: "
0982:                            + stmt.getSelectTerms());
0983:                // So first, parse conditions, also compute the conditions string
0984:                // in the process. This could be easily written in a recursive
0985:                // algorithm, but there are too many dependencies.
0986:                StringBuffer conditionPart = new StringBuffer(); // Will hold the where statement
0987:                if ((expression != null) && (expression.size() > 0))
0988:                    conditionPart.append(" where ");
0989:                HashSet tables = new HashSet(); // Will hold table specifier terms
0990:                tables.addAll(stmt.getSelectTerms());
0991:                Vector statementValues = new Vector(); // Will hold values for '?' signs in-order
0992:                // Here comes the algorithm to walk the expression tree
0993:                if (logger.isDebugEnabled())
0994:                    logger
0995:                            .debug("original expression, that will be translated to sql: "
0996:                                    + expression);
0997:                Stack openNodes = new Stack(); // Used in traversing the expression tree
0998:                if (expression != null)
0999:                    openNodes.push(new Vector(transformExpression(expression)));
1000:                while (!openNodes.isEmpty()) {
1001:                    // Select the current expression part list
1002:                    Vector parts = (Vector) openNodes.peek();
1003:                    // If there are no parts, then return
1004:                    if (parts.size() == 0) {
1005:                        openNodes.pop();
1006:                        if (!openNodes.isEmpty())
1007:                            conditionPart.append(")"); // Close sub-expression
1008:                        continue; // 20 goto 10
1009:                    }
1010:                    // Process the most left symbol
1011:                    Object nextPart = parts.remove(0);
1012:                    // There are basically 4 cases depending on the class of nextPart
1013:                    if (nextPart instanceof  Expression) {
1014:                        // It is an expression, so start new block and push expression
1015:                        // items
1016:                        conditionPart.append("(");
1017:                        openNodes.push(new Vector(
1018:                                transformExpression((Expression) nextPart)));
1019:                    }
1020:                    if (nextPart instanceof  String) {
1021:                        // It is an operator, so just append
1022:                        conditionPart.append(" " + nextPart.toString() + " ");
1023:                    }
1024:                    if (nextPart instanceof  ConstantTerm) {
1025:                        // It is a constant, add ? and remember value
1026:                        Object value = ((ConstantTerm) nextPart).getValue();
1027:                        if (value instanceof  Collection) {
1028:                            if (logger.isDebugEnabled())
1029:                                logger
1030:                                        .debug("detected collection constant value: "
1031:                                                + value);
1032:                            // Add multiple values as an enumeration
1033:                            Iterator valueIterator = ((Collection) value)
1034:                                    .iterator();
1035:                            conditionPart.append(" (");
1036:                            while (valueIterator.hasNext()) {
1037:                                Object singleValue = valueIterator.next();
1038:                                if (singleValue == null) {
1039:                                    conditionPart.append("null,");
1040:                                } else {
1041:                                    conditionPart.append("?,");
1042:                                    statementValues.add(singleValue);
1043:                                }
1044:                            }
1045:                            if (conditionPart
1046:                                    .charAt(conditionPart.length() - 1) == ',')
1047:                                conditionPart.delete(
1048:                                        conditionPart.length() - 1,
1049:                                        conditionPart.length());
1050:                            conditionPart.append(") ");
1051:                        } else {
1052:                            if (logger.isDebugEnabled())
1053:                                logger.debug("detected single constant value: "
1054:                                        + value);
1055:                            // Add a single value
1056:                            if (value == null) {
1057:                                conditionPart.append(" null ");
1058:                            } else {
1059:                                conditionPart.append(" ? ");
1060:                                statementValues.add(value);
1061:                            }
1062:                        }
1063:                    }
1064:                    if (nextPart instanceof  ReferenceTerm) {
1065:                        // It is a reference type, remember name
1066:                        ReferenceTerm term = (ReferenceTerm) nextPart;
1067:                        if (!stmt.getAllLeftTableTerms().contains(term)) {
1068:                            if (logger.isDebugEnabled())
1069:                                logger.debug("adding table: " + term + "."
1070:                                        + term.getColumnName()
1071:                                        + ", as all left terms are: "
1072:                                        + stmt.getAllLeftTableTerms());
1073:                            tables.add(term);
1074:                        }
1075:                        conditionPart.append(" " + term.getName() + "."
1076:                                + term.getColumnName() + " ");
1077:                    }
1078:                }
1079:                // Determine whether select will be distinct. Currently it is _not_
1080:                // distinct if some field returned would be blob type.
1081:                boolean isDistinct = !types.contains(byte[].class);
1082:                // Create tables full part (with left joins)
1083:                logger.debug("creating tables part...");
1084:                StringBuffer tablesPart = new StringBuffer();
1085:                Iterator tableIterator = tables.iterator();
1086:                while (tableIterator.hasNext()) {
1087:                    TableTerm tableTerm = (TableTerm) tableIterator.next();
1088:                    if (stmt.getAllLeftTableTerms().contains(tableTerm))
1089:                        continue; // Do not include tables which will be included as left terms
1090:                    tablesPart.append(getTableDeclaration(tableTerm
1091:                            .getTableName(), tableTerm.getAlias()));
1092:                    if (stmt.getSelectTerms().contains(tableTerm)) {
1093:                        // This is a selected table, so include left terms
1094:                        for (int i = 0; i < tableTerm.getLeftTableTerms()
1095:                                .size(); i++) {
1096:                            TableTerm leftTableTerm = (TableTerm) tableTerm
1097:                                    .getLeftTableTerms().get(i);
1098:                            String leftTableName = null;
1099:                            tablesPart.append(" left join ");
1100:                            tablesPart.append(getTableDeclaration(leftTableTerm
1101:                                    .getTableName(), leftTableTerm.getAlias()));
1102:                            leftTableName = leftTableTerm.getTableName();
1103:                            tablesPart.append(" on (" + tableTerm.getName()
1104:                                    + ".persistence_id = "
1105:                                    + leftTableTerm.getName()
1106:                                    + ".persistence_id)");
1107:                        }
1108:                    }
1109:                    tablesPart.append(",");
1110:                }
1111:                tablesPart.delete(tablesPart.length() - 1, tablesPart.length());
1112:                // Calculate order by
1113:                StringBuffer orderByTerm = new StringBuffer();
1114:                if ((orderBys != null) && (orderBys.size() > 0)) {
1115:                    orderByTerm.append(" order by ");
1116:                    for (int i = 0; i < orderBys.size(); i++) {
1117:                        OrderBy orderBy = (OrderBy) orderBys.get(i);
1118:                        ReferenceTerm orderReferenceTerm = new ReferenceTerm(
1119:                                orderBy.getReferenceTerm());
1120:                        if (((stmt.getMode() == QueryStatement.MODE_FIND) && (!stmt
1121:                                .getSelectTerms().contains(
1122:                                        orderBy.getReferenceTerm())))
1123:                                || ((stmt.getMode() == QueryStatement.MODE_VIEW) && (!containsReferenceTerm(
1124:                                        stmt.getSelectTerms(), orderBy
1125:                                                .getReferenceTerm())))) {
1126:                            // Order by is not referencing the main tables. To maintain
1127:                            // distinct select, we must add this attribute to result
1128:                            querySource += "," + orderReferenceTerm.getName()
1129:                                    + "." + orderReferenceTerm.getColumnName()
1130:                                    + " as ordercol" + i;
1131:                            orderReferenceTerm.setColumnAlias("ordercol" + i);
1132:                            Class attributeType = getAttributeType(
1133:                                    orderReferenceTerm.getTableName(),
1134:                                    orderReferenceTerm.getColumnName());
1135:                            types.add(attributeType);
1136:                        }
1137:                        // Add to order term
1138:                        if (orderReferenceTerm.getColumnAlias() != null)
1139:                            orderByTerm.append(orderReferenceTerm
1140:                                    .getColumnAlias());
1141:                        else
1142:                            orderByTerm.append(orderReferenceTerm.getName()
1143:                                    + "." + orderReferenceTerm.getColumnName());
1144:                        orderByTerm
1145:                                .append(orderBy.getDirection() == OrderBy.ASCENDING ? " asc,"
1146:                                        : " desc,");
1147:                    }
1148:                    orderByTerm.delete(orderByTerm.length() - 1, orderByTerm
1149:                            .length());
1150:                }
1151:                if (logger.isDebugEnabled())
1152:                    logger.debug("select types: " + types);
1153:                // Create count statement
1154:                StringBuffer subStatement = new StringBuffer("select "
1155:                        + (isDistinct ? "distinct " : "") + querySource
1156:                        + " from ");
1157:                subStatement.append(tablesPart.toString());
1158:                subStatement.append(conditionPart.toString());
1159:                StringBuffer statement = new StringBuffer(
1160:                        getCountStatement(subStatement.toString()));
1161:                logger.debug("preparing counting statement: "
1162:                        + statement.toString());
1163:                String countStatement = statement.toString();
1164:                // Prepare count statement
1165:                PreparedStatement countPstmt;
1166:                try {
1167:                    countPstmt = connection.prepareStatement(countStatement);
1168:                    for (int i = 0; i < statementValues.size(); i++) {
1169:                        Object value = getSQLValue(statementValues.get(i));
1170:                        logger.debug("setting statement parameter #" + i + ": "
1171:                                + value);
1172:                        sqlLogger.debug("setting statement parameter #" + i
1173:                                + ": " + value);
1174:                        countPstmt.setObject(i + 1, value);
1175:                    }
1176:                } catch (Exception e) {
1177:                    throw new StoreException("cannot prepare statement: "
1178:                            + statement.toString(), e);
1179:                }
1180:                // Now create full statement (add order and limit)
1181:                statement = new StringBuffer("select "
1182:                        + (isDistinct ? "distinct " : "") + querySource
1183:                        + " from ");
1184:                statement.append(tablesPart.toString());
1185:                statement.append(conditionPart.toString());
1186:                statement.append(orderByTerm.toString());
1187:                // Add limits
1188:                if (limits != null)
1189:                    statement = new StringBuffer(getLimitStatement(statement
1190:                            .toString(), limits, types));
1191:                // Run statement
1192:                Vector result = new Vector();
1193:                PreparedStatement pstmt = null;
1194:                if ((limits == null) || (!limits.isEmpty())) {
1195:                    // Prepare
1196:                    try {
1197:                        pstmt = connection.prepareStatement(statement
1198:                                .toString());
1199:                        for (int i = 0; i < statementValues.size(); i++) {
1200:                            Object value = getSQLValue(statementValues.get(i));
1201:                            logger.debug("setting statement parameter #" + i
1202:                                    + ": " + value);
1203:                            sqlLogger.debug("setting statement parameter #" + i
1204:                                    + ": " + value);
1205:                            pstmt.setObject(i + 1, value);
1206:                        }
1207:                    } catch (Exception e) {
1208:                        throw new StoreException("cannot prepare statement: "
1209:                                + statement.toString(), e);
1210:                    }
1211:                    // Execute
1212:                    try {
1213:                        sqlLogger.debug("running select statement: "
1214:                                + statement.toString());
1215:                        long startTime = System.currentTimeMillis();
1216:                        prepareStatement(pstmt, limits);
1217:                        ResultSet rs = pstmt.executeQuery();
1218:                        long endTime = System.currentTimeMillis();
1219:                        perfLogger.log("Query statement execution",
1220:                                new int[] { (int) (endTime - startTime) });
1221:                        stats.setSelectCount(stats.getSelectCount() + 1);
1222:                        stats.setSelectTime(stats.getSelectTime()
1223:                                + (endTime - startTime));
1224:                        ResultSetMetaData rsmd = rs.getMetaData();
1225:                        // Get result and pack the attributes into a map
1226:                        prepareResultSet(rs, limits);
1227:                        while (rs.next()) {
1228:                            Map attributes = new HashMap();
1229:                            for (int i = 0; i < rsmd.getColumnCount(); i++) {
1230:                                String columnName = rsmd.getColumnName(i + 1)
1231:                                        .toLowerCase();
1232:                                Object columnValue = getJavaValue(rs
1233:                                        .getObject(i + 1), rsmd
1234:                                        .getColumnType(i + 1), (Class) types
1235:                                        .get(i));
1236:                                if (columnName.startsWith("persistence_start")) {
1237:                                    // Handle persistence_starts
1238:                                    Long previousValue = (Long) attributes
1239:                                            .get("persistence_start");
1240:                                    if ((previousValue == null)
1241:                                            || (previousValue.longValue() < ((Long) columnValue)
1242:                                                    .longValue()))
1243:                                        attributes.put("persistence_start",
1244:                                                columnValue);
1245:                                } else if (columnName
1246:                                        .startsWith("persistence_end")) {
1247:                                    // Handle persistence_ends
1248:                                    Long previousValue = (Long) attributes
1249:                                            .get("persistence_end");
1250:                                    if ((previousValue == null)
1251:                                            || (previousValue.longValue() > ((Long) columnValue)
1252:                                                    .longValue()))
1253:                                        attributes.put("persistence_end",
1254:                                                columnValue);
1255:                                } else {
1256:                                    // Normal attributes
1257:                                    attributes.put(columnName, columnValue);
1258:                                }
1259:                            }
1260:                            result.add(attributes);
1261:                        }
1262:                        rs.close();
1263:                    } catch (Exception e) {
1264:                        throw new StoreException("exception while sql select",
1265:                                e);
1266:                    } finally {
1267:                        try {
1268:                            pstmt.close();
1269:                        } catch (Exception e) {
1270:                            logger.debug("unable to close statement", e);
1271:                        }
1272:                    }
1273:                } // End valid limits (running of query)
1274:                // Execute count statement if necessary
1275:                long resultSize;
1276:                if (limits == null) {
1277:                    // If there are no limits, the result is a full result
1278:                    resultSize = result.size();
1279:                } else if (limits.getSize() >= 0) {
1280:                    // Size already known (possibly from previous select)
1281:                    resultSize = limits.getSize();
1282:                } else if ((limits.getLimit() > 0)
1283:                        && (result.size() < limits.getLimit())) {
1284:                    // Count statement not necessary, this is the last page, we can
1285:                    // compute the size
1286:                    resultSize = limits.getOffset() + result.size();
1287:                } else {
1288:                    // We must get the full size the hard way, so select
1289:                    if (sqlLogger.isDebugEnabled())
1290:                        sqlLogger.debug("running count statement: "
1291:                                + countStatement);
1292:                    try {
1293:                        long startTime = System.currentTimeMillis();
1294:                        ResultSet rs = countPstmt.executeQuery();
1295:                        long endTime = System.currentTimeMillis();
1296:                        perfLogger.log("Query count statement execution",
1297:                                new int[] { (int) (endTime - startTime) });
1298:                        stats.setSelectCount(stats.getSelectCount() + 1);
1299:                        stats.setSelectTime(stats.getSelectTime()
1300:                                + (endTime - startTime));
1301:                        rs.next();
1302:                        resultSize = rs.getLong(1);
1303:                        rs.close();
1304:                    } catch (Exception e) {
1305:                        throw new StoreException(
1306:                                "exception while sql select count", e);
1307:                    } finally {
1308:                        try {
1309:                            countPstmt.close();
1310:                        } catch (Exception e) {
1311:                            logger.debug("unable to close statement", e);
1312:                        }
1313:                    }
1314:                }
1315:                // Assemble result and return
1316:                searchResult.setResultSize(resultSize);
1317:                searchResult.setResult(result);
1318:                sqlLogger.debug("returning result, size: " + result.size()
1319:                        + " / " + resultSize);
1320:                return stats;
1321:            }
1322:
1323:            /**
1324:             * Convert incoming value from database into java format.
1325:             */
1326:            protected Object getJavaValue(Object value, int type, Class javaType) {
1327:                if (value instanceof  Timestamp)
1328:                    return new Date(((Timestamp) value).getTime());
1329:                return value;
1330:            }
1331:
1332:            /**
1333:             * Convert incoming values from java into database acceptable format.
1334:             */
1335:            protected Object getSQLValue(Object value) {
1336:                if ((value != null) && (value instanceof  Date))
1337:                    return new Timestamp(((Date) value).getTime());
1338:                return value;
1339:            }
1340:
1341:            /**
1342:             * Get the class for an sql type.
1343:             */
1344:            protected String getSQLTypeName(int sqltype) {
1345:                switch (sqltype) {
1346:                case Types.DATE:
1347:                case Types.TIME:
1348:                case Types.TIMESTAMP:
1349:                    return "timestamp";
1350:                case Types.LONGVARCHAR:
1351:                case Types.VARCHAR:
1352:                    return "text";
1353:                case Types.BIT:
1354:                case Types.BOOLEAN:
1355:                    return "boolean";
1356:                case Types.INTEGER:
1357:                case Types.NUMERIC:
1358:                case Types.DECIMAL:
1359:                    return "integer";
1360:                case Types.BIGINT:
1361:                    return "bigint";
1362:                case Types.SMALLINT:
1363:                case Types.TINYINT:
1364:                    return "smallint";
1365:                case Types.DOUBLE:
1366:                case Types.FLOAT:
1367:                case Types.REAL:
1368:                    return "float";
1369:                case Types.CHAR:
1370:                    return "char";
1371:                case Types.BLOB:
1372:                case Types.BINARY:
1373:                case Types.VARBINARY:
1374:                case Types.LONGVARBINARY:
1375:                    return "blob";
1376:                default:
1377:                }
1378:                throw new StoreException(
1379:                        "no sql type definition programmed for type: "
1380:                                + sqltype);
1381:            }
1382:
1383:            /**
1384:             * Get the sql type string for a class.
1385:             */
1386:            protected int getSQLType(Class type) {
1387:                if ((Date.class.equals(type)))
1388:                    return Types.TIMESTAMP;
1389:                if (String.class.equals(type))
1390:                    return Types.VARCHAR;
1391:                if ((boolean.class.equals(type))
1392:                        || (Boolean.class.equals(type)))
1393:                    return Types.BOOLEAN;
1394:                if ((int.class.equals(type)) || (Integer.class.equals(type)))
1395:                    return Types.INTEGER;
1396:                if ((long.class.equals(type)) || (Long.class.equals(type)))
1397:                    return Types.BIGINT;
1398:                if ((byte.class.equals(type)) || (Byte.class.equals(type)))
1399:                    return Types.SMALLINT;
1400:                if (byte[].class.equals(type))
1401:                    return Types.BLOB;
1402:                if ((double.class.equals(type)) || (Double.class.equals(type)))
1403:                    return Types.DOUBLE;
1404:                if ((float.class.equals(type)) || (Float.class.equals(type)))
1405:                    return Types.FLOAT;
1406:                if ((short.class.equals(type)) || (Short.class.equals(type)))
1407:                    return Types.SMALLINT;
1408:                if ((char.class.equals(type)) || (Character.class.equals(type)))
1409:                    return Types.CHAR;
1410:                throw new StoreException("type: " + type
1411:                        + " is not an allowed primitive type.");
1412:            }
1413:
1414:            /**
1415:             * Execute update statement. Simply and safely execute the
1416:             * given statement.
1417:             * @param connection The connection to execute statement on.
1418:             * @param statement The statement to execute.
1419:             */
1420:            protected void executeUpdate(Connection connection, String statement) {
1421:                sqlLogger.debug("executing update statement: " + statement);
1422:                // Prepare
1423:                PreparedStatement pstmt;
1424:                try {
1425:                    pstmt = connection.prepareStatement(statement.toString());
1426:                } catch (Exception e) {
1427:                    throw new StoreException("cannot prepare statement: "
1428:                            + statement.toString(), e);
1429:                }
1430:                // Execute
1431:                try {
1432:                    pstmt.executeUpdate();
1433:                } catch (Exception e) {
1434:                    throw new StoreException(
1435:                            "exception while executing statement: " + statement,
1436:                            e);
1437:                } finally {
1438:                    try {
1439:                        pstmt.close();
1440:                    } catch (Exception e) {
1441:                        logger.debug("unable to close statement", e);
1442:                    }
1443:                }
1444:            }
1445:
1446:            /**
1447:             * Prepare the sql statment to be executed.
1448:             */
1449:            protected void prepareStatement(PreparedStatement pstmt,
1450:                    Limits limits) throws SQLException {
1451:            }
1452:
1453:            /**
1454:             * Prepare the result set to be iterated.
1455:             */
1456:            protected void prepareResultSet(ResultSet rs, Limits limits)
1457:                    throws SQLException {
1458:            }
1459:
1460:            /**
1461:             * The generic implementation holds no resources, so do nothing.
1462:             */
1463:            public void release(ConnectionSource source) {
1464:            }
1465:
1466:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.