0001: /**
0002: * Copyright (C) 2006, 2007 David Bulmore, Software Sensation Inc.
0003: * All Rights Reserved.
0004: *
0005: * This file is part of JPersist.
0006: *
0007: * JPersist is free software; you can redistribute it and/or modify it under
0008: * the terms of the GNU General Public License (Version 2) as published by
0009: * the Free Software Foundation.
0010: *
0011: * JPersist is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with JPersist; if not, write to the Free Software Foundation,
0018: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
0019: */package jpersist;
0020:
0021: import java.lang.reflect.Array;
0022: import jcommontk.object.ObjectFiller;
0023: import java.lang.reflect.InvocationTargetException;
0024: import java.lang.reflect.Method;
0025: import java.sql.SQLException;
0026: import java.util.Collection;
0027: import java.util.HashMap;
0028: import java.util.HashSet;
0029: import java.util.Iterator;
0030: import java.util.Map;
0031: import java.util.Set;
0032: import java.util.Vector;
0033: import java.util.logging.Level;
0034: import java.util.logging.Logger;
0035: import java.util.zip.CRC32;
0036: import jcommontk.object.ObjectConverter;
0037: import jcommontk.object.ObjectFiller.GetHandler;
0038: import jcommontk.object.ObjectFiller.ItemNotFoundException;
0039: import jcommontk.utils.StringUtils;
0040: import jpersist.interfaces.ColumnMapping;
0041: import jpersist.interfaces.GeneratedKeys;
0042: import jpersist.annotations.GlobalDelete;
0043: import jpersist.annotations.GlobalUpdate;
0044: import jpersist.annotations.UpdateNullValues;
0045: import jpersist.annotations.SingleTableInheritance;
0046: import jpersist.annotations.ConcreteTableInheritance;
0047:
0048: // TODO check parent id for null value
0049: @SuppressWarnings("unchecked")
0050: // working to complete a Java 1.5 version
0051: final class ObjectSupport {
0052: private static Logger logger = Logger.getLogger(ObjectSupport.class
0053: .getName());
0054:
0055: static Result queryObject(Database db, Class cs, Object object,
0056: Set nullValuesToInclude, boolean idColumnsOnly,
0057: String externalClauses, Object[] parameters)
0058: throws JPersistException, SQLException,
0059: IllegalAccessException, InvocationTargetException {
0060: if (logger.isLoggable(Level.FINER))
0061: logger.finer("Querying object: class " + cs.getName()
0062: + "\nexternalClauses = " + externalClauses
0063: + "\nparameters[] = "
0064: + StringUtils.toString(parameters)
0065: + "IdColumnsOnly = " + idColumnsOnly);
0066:
0067: if (cs == null && object == null)
0068: throw new JPersistException("object is null");
0069:
0070: StringBuffer sqlStatement = new StringBuffer(), externalClausesStrBuf = null;
0071: Vector values = new Vector();
0072:
0073: if (externalClauses == null
0074: || !externalClauses.startsWith("select")) {
0075: StringBuffer columnsStrBuf = new StringBuffer(), fromStrBuf = new StringBuffer(), whereStrBuf = new StringBuffer();
0076:
0077: if (externalClauses != null)
0078: externalClausesStrBuf = new StringBuffer(
0079: externalClauses);
0080:
0081: processClasses(db, cs, object, true, idColumnsOnly, false,
0082: false, nullValuesToInclude, new QueryObjectHandler(
0083: db, fromStrBuf, columnsStrBuf, whereStrBuf,
0084: externalClausesStrBuf, values));
0085:
0086: sqlStatement.append("select ").append(columnsStrBuf);
0087:
0088: if (externalClauses == null
0089: || !externalClauses.startsWith("from")) {
0090: sqlStatement.append(" from ").append(fromStrBuf);
0091:
0092: if (whereStrBuf.length() > 0) {
0093: if (externalClauses == null
0094: || !externalClauses.startsWith("where"))
0095: sqlStatement.append(" where ").append(
0096: whereStrBuf);
0097: } else if (idColumnsOnly)
0098: throw new JPersistException(
0099: "useIdColumnsOnly is defined, but there are no Id field values available");
0100: }
0101: }
0102:
0103: if (externalClauses != null) {
0104: sqlStatement.append(" " + externalClausesStrBuf);
0105:
0106: if (parameters != null)
0107: for (int i = 0; i < parameters.length; i++)
0108: values.add(parameters[i]);
0109: }
0110:
0111: if (values.size() > 0)
0112: return db.parameterizedQuery(sqlStatement.toString(),
0113: values.toArray()).setClass(cs);
0114: else
0115: return db.executeQuery(sqlStatement.toString())
0116: .setClass(cs);
0117: }
0118:
0119: static class QueryObjectHandler implements ClassHandler {
0120: Database db;
0121: StringBuffer fromStrBuf, columnsStrBuf, whereStrBuf,
0122: externalClausesStrBuf;
0123: String identifierQuoteString;
0124: Set selectSet = new HashSet(), whereSet = new HashSet();
0125: Map lastTable = new HashMap();
0126: Vector values;
0127:
0128: public QueryObjectHandler(Database db, StringBuffer fromStrBuf,
0129: StringBuffer columnsStrBuf, StringBuffer whereStrBuf,
0130: StringBuffer externalClausesStrBuf, Vector values)
0131: throws JPersistException {
0132: this .db = db;
0133: this .fromStrBuf = fromStrBuf;
0134: this .columnsStrBuf = columnsStrBuf;
0135: this .whereStrBuf = whereStrBuf;
0136: this .externalClausesStrBuf = externalClausesStrBuf;
0137: this .values = values;
0138:
0139: this .identifierQuoteString = db.getMetaData()
0140: .getIdentifierQuoteString();
0141: }
0142:
0143: public void processClass(Class objectClass, Object object,
0144: MetaData.Table table, int numberTables,
0145: char tableAlias, Map valuesMap, Set selectableColumns)
0146: throws JPersistException, SQLException,
0147: IllegalAccessException, InvocationTargetException {
0148: fromStrBuf.append((fromStrBuf.length() > 0 ? ", " : "")
0149: + identifierQuoteString + table.getTableName()
0150: + identifierQuoteString);
0151:
0152: if (numberTables > 1)
0153: fromStrBuf.append(" " + tableAlias);
0154:
0155: if (externalClausesStrBuf != null)
0156: processExternalClauses(externalClausesStrBuf, table, db
0157: .getColumnMapper(), object,
0158: identifierQuoteString);
0159:
0160: processSelect(selectableColumns, table, numberTables,
0161: tableAlias);
0162: processWhere(valuesMap, table, numberTables, tableAlias);
0163: processJoin(objectClass, table, numberTables, tableAlias);
0164: }
0165:
0166: void processSelect(Set selectableColumns, MetaData.Table table,
0167: int numberTables, char tableAlias) {
0168: for (Iterator it = selectableColumns.iterator(); it
0169: .hasNext();) {
0170: MetaData.Table.Column column = (MetaData.Table.Column) it
0171: .next();
0172: MetaData.Table.Key key = (MetaData.Table.Key) table
0173: .getImportedKeys().get(column.getColumnName());
0174:
0175: if (!selectSet.contains(column.getColumnName())
0176: && (key == null || !selectSet.contains(key
0177: .getForeignColumnName()))) {
0178: selectSet.add(column.getColumnName());
0179:
0180: if (columnsStrBuf.length() > 0)
0181: columnsStrBuf.append(", ");
0182:
0183: if (numberTables > 1)
0184: columnsStrBuf.append(tableAlias + ".");
0185:
0186: columnsStrBuf.append(identifierQuoteString
0187: + column.getColumnName()
0188: + identifierQuoteString);
0189: }
0190: }
0191: }
0192:
0193: void processWhere(Map valuesMap, MetaData.Table table,
0194: int numberTables, char tableAlias) {
0195: for (Iterator it = valuesMap.entrySet().iterator(); it
0196: .hasNext();) {
0197: Map.Entry entry = (Map.Entry) it.next();
0198: MetaData.Table.Column column = (MetaData.Table.Column) entry
0199: .getKey();
0200: MetaData.Table.Key key = (MetaData.Table.Key) table
0201: .getImportedKeys().get(column.getColumnName());
0202:
0203: if (!whereSet.contains(column.getColumnName())
0204: && column.isSearchable()
0205: && (key == null || !whereSet.contains(key
0206: .getForeignColumnName()))) {
0207: whereSet.add(column.getColumnName());
0208:
0209: if (whereStrBuf.length() > 0)
0210: whereStrBuf.append(" and ");
0211:
0212: if (numberTables > 1)
0213: whereStrBuf.append(tableAlias + ".");
0214:
0215: Object obj = entry.getValue();
0216:
0217: if (obj instanceof NullValue)
0218: whereStrBuf.append(identifierQuoteString
0219: + column.getColumnName()
0220: + identifierQuoteString + " is null");
0221: else {
0222: values.add(obj);
0223:
0224: whereStrBuf
0225: .append(identifierQuoteString
0226: + column.getColumnName()
0227: + identifierQuoteString
0228: + (hasWildCards(obj.toString()) ? " like ?"
0229: : " = ?"));
0230: }
0231: }
0232: }
0233: }
0234:
0235: void processJoin(Class cs, MetaData.Table table,
0236: int numberTables, char tableAlias)
0237: throws JPersistException {
0238: if (numberTables > 1) {
0239: MetaData.Table last = (MetaData.Table) lastTable
0240: .get("table");
0241:
0242: if (last != null) {
0243: Set keys = getMatchingImportedExportedKeys(last
0244: .getExportedKeys(), table.getImportedKeys());
0245:
0246: if (keys.size() == 0)
0247: throw new JPersistException(
0248: "The inheritance relation represented by "
0249: + cs.getName()
0250: + " does not have primary/foreign key relationships defined for the underlying"
0251: + " tables (must have a FOREIGN KEY ... REFERENCES ... clause in table creation, see alter table)");
0252:
0253: for (Iterator it2 = keys.iterator(); it2.hasNext();) {
0254: MetaData.Table.Key key = (MetaData.Table.Key) it2
0255: .next();
0256:
0257: whereStrBuf
0258: .append((whereStrBuf.length() > 0 ? " and "
0259: : "")
0260: + (char) (tableAlias - 1)
0261: + "."
0262: + identifierQuoteString
0263: + key.getForeignColumnName()
0264: + identifierQuoteString
0265: + " = "
0266: + tableAlias
0267: + "."
0268: + identifierQuoteString
0269: + key.getLocalColumnName()
0270: + identifierQuoteString);
0271: }
0272: }
0273:
0274: lastTable.put("table", table);
0275: }
0276: }
0277: }
0278:
0279: static Object loadObject(Result result, Object object,
0280: boolean loadAssociations) throws JPersistException,
0281: SQLException, IllegalAccessException,
0282: InstantiationException, InvocationTargetException {
0283: if (logger.isLoggable(Level.FINER))
0284: logger.finer("Loading object of class "
0285: + object.getClass().getName());
0286:
0287: if (object == null)
0288: throw new JPersistException("data object is null");
0289:
0290: if (object instanceof PersistentObject)
0291: ((PersistentObject) object).makeObjectTransient();
0292:
0293: processClasses(result.getDatabase(), object.getClass(), object,
0294: false, false, false, false, null, new LoadClassHandler(
0295: result, object));
0296:
0297: if (object instanceof PersistentObject)
0298: ((PersistentObject) object)
0299: .setObjectChecksum(calculateChecksum(result
0300: .getDatabase(), object));
0301:
0302: if (loadAssociations)
0303: if (!(object instanceof PersistentObject && ((PersistentObject) object)
0304: .getIgnoreAssociations()))
0305: loadAssociations(result.getDatabase(), object);
0306:
0307: return object;
0308: }
0309:
0310: static class LoadClassHandler implements ClassHandler {
0311: Result result;
0312: Object object;
0313:
0314: LoadClassHandler(Result result, Object object) {
0315: this .result = result;
0316: this .object = object;
0317: }
0318:
0319: public void processClass(Class objectClass,
0320: final Object object, final MetaData.Table table,
0321: int numberTables, final char tableAlias, Map valuesMap,
0322: Set selectableColumns) throws JPersistException,
0323: SQLException, IllegalAccessException,
0324: InvocationTargetException {
0325: if (objectClass
0326: .isAnnotationPresent(SingleTableInheritance.class)
0327: || objectClass
0328: .isAnnotationPresent(ConcreteTableInheritance.class))
0329: objectClass = null;
0330:
0331: if (table != null)
0332: loadObjectsWithTableHelp(objectClass, object, table,
0333: tableAlias);
0334: else
0335: loadObjectsWithoutTableHelp(objectClass, object);
0336: }
0337:
0338: void loadObjectsWithTableHelp(Class objectClass,
0339: final Object object, final MetaData.Table table,
0340: final char tableAlias) throws IllegalAccessException,
0341: InvocationTargetException, JPersistException {
0342: if (object instanceof PersistentObject)
0343: if (table.getPrimaryKeys().size() == 0)
0344: ((PersistentObject) object)
0345: .setObjectPersistence(PersistentObject.MISSING_ROW_ID);
0346:
0347: ObjectFiller.fillObject(new GetHandler() {
0348: public Object get(String key, Class objectType)
0349: throws ItemNotFoundException {
0350: try {
0351: MetaData.Table.Column column = table.getColumn(
0352: result.getDatabase().getColumnMapper(),
0353: key, object);
0354:
0355: if (column != null) {
0356: String columnName = column.getColumnName();
0357: Object value = null;
0358:
0359: try {
0360: value = result.getColumnValue(
0361: objectType, columnName);
0362: } catch (Exception e) {
0363: value = result.getColumnValue(
0364: objectType, tableAlias + "."
0365: + columnName);
0366: }
0367:
0368: return value;
0369: }
0370: } catch (Exception e) {
0371: logger.log(Level.WARNING, e.toString(), e);
0372: }
0373:
0374: throw new ObjectFiller.ItemNotFoundException();
0375: }
0376: }, object, objectClass, true, false, null, true);
0377:
0378: loadPrimaryKeys(table, tableAlias);
0379: }
0380:
0381: void loadPrimaryKeys(MetaData.Table table, char tableAlias)
0382: throws JPersistException {
0383: if (object instanceof PersistentObject) {
0384: Set ids = new HashSet(table.getPrimaryKeys().keySet());
0385:
0386: for (Iterator it = ids.iterator(); it.hasNext();) {
0387: String columnName = (String) it.next();
0388: Object value = null;
0389:
0390: try {
0391: value = result.getColumnValue(columnName);
0392: } catch (Exception e) {
0393: value = result.getColumnValue(tableAlias + "."
0394: + columnName);
0395: }
0396:
0397: ((PersistentObject) object).getObjectKeyValues()
0398: .put(columnName, value);
0399: }
0400: }
0401: }
0402:
0403: void loadObjectsWithoutTableHelp(Class objectClass,
0404: final Object object) throws IllegalAccessException,
0405: InvocationTargetException {
0406: if (logger.isLoggable(Level.FINER))
0407: logger.finer("table not found for class "
0408: + object.getClass().getName());
0409:
0410: if (object instanceof PersistentObject)
0411: ((PersistentObject) object)
0412: .setObjectPersistence(PersistentObject.NO_TABLE_INFO);
0413:
0414: ObjectFiller.fillObject(new GetHandler() {
0415: public Object get(String key, Class objectType) {
0416: MetaData metaData = null;
0417: Object obj = null;
0418: String name = null;
0419:
0420: try {
0421: metaData = result.getDatabase().getMetaData();
0422:
0423: name = key;
0424: name = metaData.getStoresCase() == MetaData.STORES_UNKNOWN
0425: || metaData.getStoresCase() == MetaData.STORES_LOWERCASE ? name
0426: .toUpperCase()
0427: : name.toLowerCase();
0428:
0429: obj = result.getColumnValue(objectType, name);
0430: } catch (Exception e) {
0431: try {
0432: name = key;
0433: name = metaData.getStoresCase() == MetaData.STORES_UNKNOWN
0434: || metaData.getStoresCase() == MetaData.STORES_LOWERCASE ? StringUtils
0435: .camelCaseToUpperCaseUnderline(name)
0436: : StringUtils
0437: .camelCaseToLowerCaseUnderline(name);
0438:
0439: obj = result.getColumnValue(objectType,
0440: name);
0441: } catch (Exception e2) {
0442: logger.log(Level.WARNING, e2.getMessage(),
0443: e2);
0444: }
0445: }
0446:
0447: return obj;
0448: }
0449: }, object, objectClass, true, false, null, true);
0450: }
0451: }
0452:
0453: static void loadAssociations(Database db, Object object)
0454: throws JPersistException, SQLException,
0455: InstantiationException, IllegalAccessException,
0456: InvocationTargetException {
0457: Class objectClass = object.getClass();
0458: Method objectMethods[] = objectClass.getMethods();
0459:
0460: for (int i = 0; i < objectMethods.length; i++) {
0461: if (objectMethods[i].getName().equals("setDbAssociation")) {
0462: Class objectClass2 = objectMethods[i]
0463: .getParameterTypes()[0], c2Type = objectClass2
0464: .isArray() ? objectClass2.getComponentType()
0465: : objectClass2, collectionType = null;
0466:
0467: if (!(object instanceof PersistentObject)
0468: || !((PersistentObject) object)
0469: .classInIgnoreAssociation(c2Type)) {
0470: Method associationObjectMethods[] = c2Type
0471: .getMethods();
0472:
0473: if (objectMethods[i].getParameterTypes().length == 2)
0474: collectionType = objectMethods[i]
0475: .getParameterTypes()[1];
0476:
0477: Object associationObject = c2Type.newInstance();
0478:
0479: copyAssociationIds(db, object, associationObject);
0480:
0481: Result result2 = db.queryObject(associationObject);
0482:
0483: try {
0484: if (collectionType == null) {
0485: if (!objectClass2.isArray()
0486: && result2.hasNext()) {
0487: Object obj = result2.next();
0488:
0489: if (obj != null)
0490: objectMethods[i].invoke(object,
0491: new Object[] { obj });
0492: } else {
0493: Vector tmp = new Vector();
0494:
0495: while (result2.hasNext())
0496: tmp.add(result2.next());
0497:
0498: if (tmp.size() > 0)
0499: objectMethods[i]
0500: .invoke(
0501: object,
0502: new Object[] { tmp
0503: .toArray((Object[]) Array
0504: .newInstance(
0505: c2Type,
0506: tmp
0507: .size())) });
0508: }
0509: } else {
0510: Collection collection = (Collection) collectionType
0511: .newInstance();
0512:
0513: while (result2.hasNext())
0514: collection.add(result2.next());
0515:
0516: if (collection.size() > 0)
0517: objectMethods[i]
0518: .invoke(object, new Object[] {
0519: null, collection });
0520: }
0521: } finally {
0522: result2.close();
0523: }
0524: }
0525: }
0526: }
0527: }
0528:
0529: static int objectTransaction(Database db, Object object,
0530: Set nullValuesToInclude, boolean isInsertUpdate,
0531: String externalClauses, Object[] parameters)
0532: throws JPersistException, SQLException,
0533: IllegalAccessException, InvocationTargetException,
0534: InstantiationException {
0535: if (object == null)
0536: throw new JPersistException("object is null");
0537:
0538: int returnValue = 0;
0539: boolean commit = false, rollback = false;
0540:
0541: if (db.getAutoCommit()) {
0542: db.setAutoCommit(false);
0543: commit = true;
0544: }
0545:
0546: try {
0547: if (isInsertUpdate)
0548: returnValue = saveObject(db, object,
0549: nullValuesToInclude, externalClauses,
0550: parameters);
0551: else
0552: returnValue = deleteObject(db, object,
0553: nullValuesToInclude, externalClauses,
0554: parameters);
0555: } catch (Exception e) {
0556: rollback = true;
0557:
0558: if (commit)
0559: db.rollback();
0560:
0561: throw new JPersistException(e);
0562: } finally {
0563: if (commit) {
0564: if (!rollback)
0565: db.commit();
0566:
0567: db.setAutoCommit(true);
0568: }
0569:
0570: if (!rollback && isInsertUpdate
0571: && object instanceof PersistentObject
0572: && ((PersistentObject) object).getReloadAfterSave()) {
0573: try {
0574: Result result = queryObject(db, object.getClass(),
0575: object, null, true, null, (Object[]) null);
0576:
0577: try {
0578: if (result.hasNext())
0579: result.next(object);
0580: } finally {
0581: result.close();
0582: }
0583: } catch (Exception e) {
0584: String message = "Could not reload object following save. Can not make this object persistent.";
0585:
0586: if (e.getMessage() != null
0587: && e.getMessage().startsWith(
0588: "useIdColumnsOnly")
0589: && !db.getMetaData()
0590: .supportsGeneratedKeys()) {
0591: message += "\nYour database reports that it does not support retrieving auto-generated keys."
0592: + "\nTherefore, you can't make objects relying on auto-generated keys persistent following an insert."
0593: + "\nIn this case, only loaded objects can be persistent. You can either implement GeneratedKeys or set "
0594: + "PersistentObject.setReloadAfterSave(false)";
0595: }
0596:
0597: throw new JPersistException(message, e);
0598: }
0599: }
0600: }
0601:
0602: return returnValue;
0603: }
0604:
0605: static int saveObject(Database db, Object object,
0606: Set nullValuesToInclude, String externalClauses,
0607: Object[] parameters) throws JPersistException,
0608: SQLException, IllegalAccessException,
0609: InvocationTargetException, InstantiationException {
0610: int returnValue = 0;
0611: boolean persistentUpdate = (object instanceof PersistentObject
0612: && ((PersistentObject) object).getObjectChecksum() != 0 && ((PersistentObject) object)
0613: .getObjectPersistence() == PersistentObject.OBJECT_CAN_PERSIST), externalWhere = (externalClauses != null && externalClauses
0614: .toLowerCase().startsWith("where")), globalUpdate = object
0615: .getClass().isAnnotationPresent(GlobalUpdate.class);
0616:
0617: if (persistentUpdate || globalUpdate || externalWhere) {
0618: if (globalUpdate
0619: || externalWhere
0620: || ((PersistentObject) object).objectHasChanged()
0621: || calculateChecksum(db, object) != ((PersistentObject) object)
0622: .getObjectChecksum())
0623: returnValue = updateObject(db, object,
0624: nullValuesToInclude, externalClauses,
0625: parameters);
0626: } else
0627: returnValue = insertObject(db, object);
0628:
0629: returnValue += saveAssociations(db, object);
0630:
0631: return returnValue;
0632: }
0633:
0634: static int saveAssociations(Database db, Object object)
0635: throws JPersistException, SQLException,
0636: IllegalAccessException, InvocationTargetException,
0637: InstantiationException {
0638: Method methods[] = object.getClass().getMethods();
0639: boolean isPersistentObject = object instanceof PersistentObject;
0640: int returnValue = 0;
0641:
0642: for (int i = 0; i < methods.length; i++) {
0643: if (methods[i].getName().equals("getDbAssociation")) {
0644: Object associationObject = methods[i].invoke(object,
0645: new Object[] { null });
0646:
0647: if (associationObject != null)
0648: if (associationObject instanceof Collection) {
0649: Iterator it = ((Collection) associationObject)
0650: .iterator();
0651: Object obj = null;
0652:
0653: while (it.hasNext()
0654: && (obj = it.next()) != null) {
0655: if (!(isPersistentObject && ((PersistentObject) object)
0656: .classInIgnoreAssociation(obj
0657: .getClass()))) {
0658: copyAssociationIds(db, object, obj);
0659: returnValue += saveObject(db, obj,
0660: null, null, null);
0661: }
0662: }
0663: } else if (associationObject.getClass().isArray()) {
0664: Object objectArray[] = (Object[]) associationObject;
0665:
0666: for (int i2 = 0; i2 < objectArray.length; i2++) {
0667: if (!(isPersistentObject && ((PersistentObject) object)
0668: .classInIgnoreAssociation(objectArray[i2]
0669: .getClass()))) {
0670: copyAssociationIds(db, object,
0671: objectArray[i2]);
0672: returnValue += saveObject(db,
0673: objectArray[i2], null, null,
0674: null);
0675: }
0676: }
0677: } else {
0678: if (!(isPersistentObject && ((PersistentObject) object)
0679: .classInIgnoreAssociation(associationObject
0680: .getClass()))) {
0681: copyAssociationIds(db, object,
0682: associationObject);
0683: returnValue = saveObject(db,
0684: associationObject, null, null, null);
0685: }
0686: }
0687: }
0688: }
0689:
0690: return returnValue;
0691: }
0692:
0693: static int insertObject(Database db, Object object)
0694: throws JPersistException, SQLException,
0695: IllegalAccessException, InvocationTargetException,
0696: InstantiationException {
0697: if (logger.isLoggable(Level.FINER))
0698: logger.finer("inserting object of class "
0699: + object.getClass().getName());
0700:
0701: if (object == null)
0702: throw new JPersistException("object is null");
0703:
0704: Vector returnValues = new Vector();
0705:
0706: processClasses(db, object.getClass(), object, true, false,
0707: false, false, null, new InsertClassHandler(db,
0708: returnValues));
0709:
0710: if (object instanceof PersistentObject)
0711: ((PersistentObject) object)
0712: .setObjectChecksum(calculateChecksum(db, object));
0713:
0714: int returnValue = 0;
0715:
0716: for (int i = 0; i < returnValues.size(); i++)
0717: returnValue += ((Integer) returnValues.elementAt(i))
0718: .intValue();
0719:
0720: return returnValue;
0721: }
0722:
0723: static class InsertClassHandler implements ClassHandler {
0724: String identifierQuoteString;
0725: Vector returnValues;
0726: Database db;
0727:
0728: InsertClassHandler(Database db, Vector returnValues)
0729: throws JPersistException {
0730: this .db = db;
0731: this .returnValues = returnValues;
0732: this .identifierQuoteString = db.getMetaData()
0733: .getIdentifierQuoteString();
0734: }
0735:
0736: public void processClass(Class objectClass, Object object,
0737: MetaData.Table table, int numberTables,
0738: char tableAlias, Map valuesMap, Set selectableColumns)
0739: throws JPersistException, SQLException,
0740: IllegalAccessException, InvocationTargetException {
0741: StringBuffer sqlStatement = new StringBuffer("insert into "
0742: + identifierQuoteString + table.getTableName()
0743: + identifierQuoteString + " ("), columnsStrBuf = new StringBuffer(), valuesStrBuf = new StringBuffer();
0744: Vector columnValues = new Vector(), keysRequested = null, keysReturned = null;
0745:
0746: processColumns(table, valuesMap, sqlStatement,
0747: columnsStrBuf, valuesStrBuf, columnValues);
0748:
0749: String dbUrl = db.getMetaData().getDatabaseUrl()
0750: .toLowerCase(), possibleGeneratedKey = table
0751: .getPossibleGeneratedKey();
0752:
0753: if (dbUrl.startsWith("jdbc:oracle")
0754: && possibleGeneratedKey != null) {
0755: keysReturned = new Vector();
0756: keysReturned.add(possibleGeneratedKey);
0757: keysRequested = new Vector(keysReturned);
0758: }
0759:
0760: returnValues.add(new Integer(db.parameterizedUpdate(
0761: sqlStatement.toString(), keysReturned, columnValues
0762: .toArray())));
0763:
0764: processGeneratedKeys(table, db.getColumnMapper(), object,
0765: dbUrl, keysRequested, keysReturned);
0766: }
0767:
0768: void processColumns(MetaData.Table table, Map valuesMap,
0769: StringBuffer sqlStatement, StringBuffer columnsStrBuf,
0770: StringBuffer valuesStrBuf, Vector columnValues)
0771: throws JPersistException {
0772: int readOnly = 0;
0773:
0774: for (Iterator it = valuesMap.entrySet().iterator(); it
0775: .hasNext();) {
0776: Map.Entry entry = (Map.Entry) it.next();
0777: MetaData.Table.Column column = (MetaData.Table.Column) entry
0778: .getKey();
0779:
0780: if (!column.isReadOnly()) {
0781: Object obj = entry.getValue();
0782:
0783: if (!(obj instanceof NullValue)) {
0784: columnsStrBuf
0785: .append((columnsStrBuf.length() > 0 ? ", "
0786: : "")
0787: + identifierQuoteString
0788: + column.getColumnName()
0789: + identifierQuoteString);
0790: columnValues.add(obj);
0791: }
0792: } else
0793: readOnly++;
0794: }
0795:
0796: if (columnValues.size() == 0) {
0797: if (readOnly > 0)
0798: throw new JPersistException(
0799: "Table "
0800: + table.getTableName()
0801: + " appears to be readonly, might need to log in to the database");
0802: else
0803: throw new JPersistException(
0804: "There must be some number of column values to insert");
0805: }
0806:
0807: sqlStatement.append(columnsStrBuf + ") values(");
0808:
0809: for (Iterator it = columnValues.iterator(); it.hasNext(); it
0810: .next())
0811: valuesStrBuf.append((valuesStrBuf.length() > 0 ? ", "
0812: : "")
0813: + "?");
0814:
0815: sqlStatement.append(valuesStrBuf + ")");
0816: }
0817:
0818: void processGeneratedKeys(MetaData.Table table,
0819: ColumnMapping columnMapper, Object object,
0820: String dbUrl, Vector keysRequested, Vector keysReturned)
0821: throws JPersistException, IllegalAccessException,
0822: InvocationTargetException {
0823: if (object instanceof GeneratedKeys)
0824: ((GeneratedKeys) object).setGeneratedKeys(db,
0825: keysRequested, keysReturned);
0826: else if (dbUrl.startsWith("jdbc:oracle")
0827: && keysReturned != null && keysReturned.size() > 0) {
0828: if (keysRequested.size() != keysReturned.size())
0829: throw new JPersistException(
0830: "Auto-generated keys returned ("
0831: + keysReturned.size()
0832: + ") do not match the number of primary keys requested "
0833: + keysRequested
0834: + "; try implementing GeneratedKeys");
0835:
0836: setAutoGeneratedKeys(object, table, columnMapper,
0837: keysRequested, keysReturned);
0838: } else if (table.getGeneratedKey() != null) {
0839: boolean queryExecuted = false;
0840: Result result = null;
0841:
0842: if (dbUrl.startsWith("jdbc:mysql")) {
0843: result = db
0844: .executeQuery("select LAST_INSERT_ID() as id ");
0845: queryExecuted = true;
0846: } else if (dbUrl.startsWith("jdbc:derby")
0847: || dbUrl.startsWith("jdbc:db2")) {
0848: result = db
0849: .executeQuery("select IDENTITY_VAL_LOCAL() as id from "
0850: + table.getTableName());
0851: queryExecuted = true;
0852: } else if (dbUrl.startsWith("jdbc:hsqldb")
0853: || dbUrl.startsWith("jdbc:h2")) {
0854: result = db
0855: .executeQuery("select IDENTITY() as id from "
0856: + table.getTableName());
0857: queryExecuted = true;
0858: } else if (dbUrl.startsWith("jdbc:postgresql")) {
0859: result = db.executeQuery("select currval('"
0860: + table.getTableName() + '_'
0861: + table.getGeneratedKey() + "_seq"
0862: + "') as id");
0863: queryExecuted = true;
0864: }
0865:
0866: if (queryExecuted && result.hasNext()
0867: && result.next() != null) {
0868: keysReturned = new Vector();
0869: keysReturned.add(result.getColumnValue("id"));
0870:
0871: keysRequested = new Vector();
0872: keysRequested.add(table.getGeneratedKey());
0873:
0874: setAutoGeneratedKeys(object, table, db
0875: .getColumnMapper(), keysRequested,
0876: keysReturned);
0877: }
0878: }
0879: }
0880: }
0881:
0882: static void setAutoGeneratedKeys(Object object,
0883: MetaData.Table table, ColumnMapping columnMapper,
0884: Vector keysRequested, Vector keysReturned)
0885: throws IllegalAccessException, InvocationTargetException {
0886: for (int h = 0; h < keysReturned.size(); h++) {
0887: String key = (String) keysRequested.elementAt(h);
0888: Object value = keysReturned.elementAt(h);
0889:
0890: Method method = getMatchingMethod(columnMapper, key,
0891: object, false);
0892:
0893: if (method != null) {
0894: Object convertedObject = ObjectConverter.convertObject(
0895: method.getParameterTypes()[0], value);
0896:
0897: if (convertedObject != null)
0898: method.invoke(object,
0899: new Object[] { convertedObject });
0900:
0901: if (object instanceof PersistentObject)
0902: ((PersistentObject) object).getObjectKeyValues()
0903: .put(key, value);
0904: }
0905: }
0906: }
0907:
0908: static int updateObject(Database db, Object object,
0909: Set nullValuesToInclude, String externalClauses,
0910: Object[] parameters) throws JPersistException,
0911: SQLException, IllegalAccessException,
0912: InvocationTargetException {
0913: if (logger.isLoggable(Level.FINER))
0914: logger.finer("updating object of class "
0915: + object.getClass().getName()
0916: + "\nexternalClauses = " + externalClauses
0917: + "\nparameters[] = "
0918: + StringUtils.toString(parameters));
0919:
0920: if (object == null)
0921: throw new JPersistException("object is null");
0922:
0923: Vector returnValues = new Vector();
0924: Map updatedKeys = new HashMap();
0925:
0926: processClasses(db, object.getClass(), object, true, false,
0927: false, true, nullValuesToInclude,
0928: new UpdateClassHandler(db, updatedKeys, returnValues,
0929: externalClauses, parameters));
0930:
0931: if (object instanceof PersistentObject) {
0932: ((PersistentObject) object).getObjectKeyValues().putAll(
0933: updatedKeys);
0934: ((PersistentObject) object)
0935: .setObjectChecksum(calculateChecksum(db, object));
0936: }
0937:
0938: int returnValue = 0;
0939:
0940: for (int i = 0; i < returnValues.size(); i++)
0941: returnValue += ((Integer) returnValues.elementAt(i))
0942: .intValue();
0943:
0944: return returnValue;
0945: }
0946:
0947: static class UpdateClassHandler implements ClassHandler {
0948: Database db;
0949: String externalClauses, identifierQuoteString;
0950: Map updatedKeys;
0951: Object[] parameters;
0952: Vector returnValues;
0953:
0954: UpdateClassHandler(Database db, Map updatedKeys,
0955: Vector returnValues, String externalClauses,
0956: Object[] parameters) throws JPersistException {
0957: this .db = db;
0958: this .externalClauses = externalClauses;
0959: this .parameters = parameters;
0960: this .updatedKeys = updatedKeys;
0961: this .returnValues = returnValues;
0962:
0963: this .identifierQuoteString = db.getMetaData()
0964: .getIdentifierQuoteString();
0965: }
0966:
0967: public void processClass(Class objectClass, Object object,
0968: MetaData.Table table, int numberTables,
0969: char tableAlias, Map valuesMap, Set selectableColumns)
0970: throws JPersistException, SQLException,
0971: IllegalAccessException, InvocationTargetException {
0972: StringBuffer sqlStatement = new StringBuffer();
0973: Vector columnValues = new Vector(), whereValues = new Vector();
0974: StringBuffer columnsStrBuf = new StringBuffer(), whereStrBuf = new StringBuffer();
0975:
0976: for (Iterator it = valuesMap.entrySet().iterator(); it
0977: .hasNext();) {
0978: Map.Entry entry = (Map.Entry) it.next();
0979: MetaData.Table.Column column = (MetaData.Table.Column) entry
0980: .getKey();
0981: String columnName = column.getColumnName();
0982: Object obj = entry.getValue();
0983:
0984: if (!column.isReadOnly()) {
0985: if (obj instanceof NullValue)
0986: columnsStrBuf
0987: .append((columnsStrBuf.length() > 0 ? ", "
0988: : "")
0989: + identifierQuoteString
0990: + columnName
0991: + identifierQuoteString
0992: + " = null");
0993: else {
0994: columnsStrBuf
0995: .append((columnsStrBuf.length() > 0 ? ", "
0996: : "")
0997: + identifierQuoteString
0998: + columnName
0999: + identifierQuoteString
1000: + " = ?");
1001: columnValues.add(obj);
1002: }
1003: }
1004:
1005: if (object instanceof PersistentObject
1006: && ((PersistentObject) object)
1007: .getObjectKeyValue(columnName) != null)
1008: updatedKeys.put(columnName, obj);
1009:
1010: if (object instanceof PersistentObject
1011: && (obj = ((PersistentObject) object)
1012: .getObjectKeyValue(columnName)) != null)
1013: if (column.isSearchable()) {
1014: whereStrBuf
1015: .append((whereStrBuf.length() > 0 ? " and "
1016: : "")
1017: + identifierQuoteString
1018: + columnName
1019: + identifierQuoteString
1020: + (hasWildCards(obj.toString()) ? " like ?"
1021: : " = ?"));
1022: whereValues.add(obj);
1023: }
1024: }
1025:
1026: sqlStatement.append("update " + identifierQuoteString
1027: + table.getTableName() + identifierQuoteString
1028: + " set " + columnsStrBuf);
1029:
1030: if (whereStrBuf.length() > 0
1031: && (externalClauses == null || !externalClauses
1032: .startsWith("where")))
1033: sqlStatement.append(" where ").append(whereStrBuf);
1034:
1035: StringBuffer externalClausesStrBuf = null;
1036:
1037: if (externalClauses != null)
1038: processExternalClauses(
1039: externalClausesStrBuf = new StringBuffer(
1040: externalClauses), table, db
1041: .getColumnMapper(), object,
1042: identifierQuoteString);
1043:
1044: if (externalClauses != null) {
1045: sqlStatement.append(" " + externalClausesStrBuf);
1046:
1047: if (parameters != null)
1048: for (int i = 0; i < parameters.length; i++)
1049: whereValues.add(parameters[i]);
1050: }
1051:
1052: if (columnValues.size() > 0
1053: && (whereValues.size() > 0 || objectClass
1054: .isAnnotationPresent(GlobalUpdate.class))) {
1055: columnValues.addAll(whereValues);
1056: returnValues.add(new Integer(db
1057: .parameterizedUpdate(sqlStatement.toString(),
1058: columnValues.toArray())));
1059: } else
1060: throw new JPersistException(
1061: "Object does not have a where clause and does not extend GlobalUpdate");
1062: }
1063: }
1064:
1065: static int deleteObject(Database db, Object object,
1066: Set nullValuesToInclude, String externalClauses,
1067: Object[] parameters) throws JPersistException,
1068: SQLException, IllegalAccessException,
1069: InvocationTargetException {
1070: if (logger.isLoggable(Level.FINER))
1071: logger.finer("deleting object of class "
1072: + object.getClass().getName()
1073: + "\nexternalClauses = " + externalClauses
1074: + "\nparameters[] = "
1075: + StringUtils.toString(parameters));
1076:
1077: if (object == null)
1078: throw new JPersistException("object is null");
1079:
1080: Vector returnValues = new Vector();
1081:
1082: processClasses(db, object.getClass(), object, true, false,
1083: true, false, nullValuesToInclude,
1084: new DeleteClassHandler(db, returnValues,
1085: externalClauses, parameters));
1086:
1087: if (object instanceof PersistentObject)
1088: ((PersistentObject) object).makeObjectTransient();
1089:
1090: int returnValue = 0;
1091:
1092: for (int i = 0; i < returnValues.size(); i++)
1093: returnValue += ((Integer) returnValues.elementAt(i))
1094: .intValue();
1095:
1096: return returnValue;
1097: }
1098:
1099: static class DeleteClassHandler implements ClassHandler {
1100: Database db;
1101: String identifierQuoteString, externalClauses;
1102: Object[] parameters;
1103: Vector returnValues;
1104:
1105: DeleteClassHandler(Database db, Vector returnValues,
1106: String externalClauses, Object[] parameters)
1107: throws JPersistException {
1108: this .db = db;
1109: this .externalClauses = externalClauses;
1110: this .parameters = parameters;
1111: this .returnValues = returnValues;
1112:
1113: this .identifierQuoteString = db.getMetaData()
1114: .getIdentifierQuoteString();
1115: }
1116:
1117: public void processClass(Class objectClass, Object object,
1118: MetaData.Table table, int numberTables,
1119: char tableAlias, Map valuesMap, Set selectableColumns)
1120: throws JPersistException, SQLException,
1121: IllegalAccessException, InvocationTargetException {
1122: StringBuffer sqlStatement = new StringBuffer(), whereStrBuf = new StringBuffer();
1123: Vector values = new Vector();
1124:
1125: for (Iterator it = valuesMap.entrySet().iterator(); it
1126: .hasNext();) {
1127: Map.Entry entry = (Map.Entry) it.next();
1128: MetaData.Table.Column column = (MetaData.Table.Column) entry
1129: .getKey();
1130:
1131: if (column.isSearchable()) {
1132: String columnName = column.getColumnName();
1133: Object obj = entry.getValue();
1134:
1135: if (!(obj instanceof NullValue))
1136: if (!(object instanceof PersistentObject)
1137: || ((PersistentObject) object)
1138: .getObjectChecksum() == 0
1139: || (((PersistentObject) object)
1140: .getObjectChecksum() != 0 && (obj = ((PersistentObject) object)
1141: .getObjectKeyValue(columnName)) != null)) {
1142: whereStrBuf
1143: .append((whereStrBuf.length() > 0 ? " and "
1144: : "")
1145: + identifierQuoteString
1146: + columnName
1147: + identifierQuoteString
1148: + (hasWildCards(obj
1149: .toString()) ? " like ?"
1150: : " = ?"));
1151: values.add(obj);
1152: }
1153: }
1154: }
1155:
1156: sqlStatement.append("delete ");
1157:
1158: if (externalClauses == null
1159: || !externalClauses.startsWith("from")) {
1160: sqlStatement.append(" from " + identifierQuoteString
1161: + table.getTableName() + identifierQuoteString);
1162:
1163: if (whereStrBuf.length() > 0
1164: && (externalClauses == null || !externalClauses
1165: .startsWith("where")))
1166: sqlStatement.append(" where ").append(whereStrBuf);
1167: }
1168:
1169: StringBuffer externalClausesStrBuf = null;
1170:
1171: if (externalClauses != null)
1172: processExternalClauses(
1173: externalClausesStrBuf = new StringBuffer(
1174: externalClauses), table, db
1175: .getColumnMapper(), object,
1176: identifierQuoteString);
1177:
1178: if (externalClauses != null) {
1179: sqlStatement.append(" " + externalClausesStrBuf);
1180:
1181: if (parameters != null)
1182: for (int i = 0; i < parameters.length; i++)
1183: values.add(parameters[i]);
1184: }
1185:
1186: if (values.size() > 0)
1187: returnValues.add(new Integer(db.parameterizedUpdate(
1188: sqlStatement.toString(), values.toArray())));
1189: else if (!(objectClass
1190: .isAnnotationPresent(GlobalDelete.class)))
1191: throw new JPersistException(
1192: "Object does not have a where clause and does not extend GlobalDelete");
1193: else
1194: returnValues.add(new Integer(db
1195: .executeUpdate(sqlStatement.toString())));
1196: }
1197: }
1198:
1199: static void copyAssociationIds(Database db, Object object,
1200: Object associationObject) throws JPersistException,
1201: SQLException, IllegalAccessException,
1202: InvocationTargetException {
1203: Set keys = getMatchingImportedExportedKeys(
1204: getImportedExportedKeys(db, object, true),
1205: getImportedExportedKeys(db, associationObject, false));
1206:
1207: if (keys.size() != 0) {
1208: for (Iterator it = keys.iterator(); it.hasNext();) {
1209: MetaData.Table.Key key = (MetaData.Table.Key) it.next();
1210:
1211: Method getMethod = getMatchingMethod(db
1212: .getColumnMapper(), key.getForeignColumnName(),
1213: object, true), setMethod = getMatchingMethod(db
1214: .getColumnMapper(), key.getLocalColumnName(),
1215: associationObject, false);
1216:
1217: if (getMethod == null)
1218: throw new JPersistException(
1219: "Could not locate method for column "
1220: + key.getForeignColumnName());
1221: if (setMethod == null)
1222: throw new JPersistException(
1223: "Could not locate method for column "
1224: + key.getLocalColumnName());
1225:
1226: Object convertedObject = ObjectConverter.convertObject(
1227: setMethod.getParameterTypes()[0], getMethod
1228: .invoke(object, (Object[]) null));
1229:
1230: if (convertedObject == null)
1231: throw new JPersistException(
1232: "Could not match primary/foreign keys association relation represented by "
1233: + object.getClass().getName());
1234:
1235: setMethod.invoke(associationObject,
1236: new Object[] { convertedObject });
1237: }
1238: } else {
1239: throw new JPersistException(
1240: "The association relation represented by "
1241: + object.getClass().getName()
1242: + " <-- "
1243: + associationObject.getClass().getName()
1244: + " does not have primary/foreign key relationships defined for the underlying"
1245: + " tables (must have a FOREIGN KEY ... REFERENCES ... clause in table creation, see alter table)");
1246: }
1247: }
1248:
1249: static Method getMatchingMethod(ColumnMapping columnMapper,
1250: String columnName, Object object, boolean getMethod) {
1251: Method methods[] = object.getClass().getMethods();
1252: String matchName = MetaData.normalizeName(columnName);
1253:
1254: for (int i = 0; i < methods.length; i++)
1255: if ((getMethod && methods[i].getName().startsWith("get"))
1256: || (!getMethod && methods[i].getName().startsWith(
1257: "set")))
1258: if (MetaData.normalizeName(
1259: methods[i].getName().substring(3)).indexOf(
1260: matchName) > -1)
1261: return methods[i];
1262:
1263: String name = null;
1264:
1265: if (object instanceof ColumnMapping
1266: && (name = ((ColumnMapping) object)
1267: .getTableColumnName(columnName.toLowerCase())) != null)
1268: matchName = name;
1269: else if (columnMapper != null
1270: && (name = columnMapper.getTableColumnName(columnName
1271: .toLowerCase())) != null)
1272: matchName = name;
1273:
1274: for (int i = 0; i < methods.length; i++)
1275: if ((getMethod && methods[i].getName().startsWith("get"))
1276: || (!getMethod && methods[i].getName().startsWith(
1277: "set")))
1278: if (MetaData.normalizeName(
1279: methods[i].getName().substring(3)).indexOf(
1280: matchName) > -1)
1281: return methods[i];
1282:
1283: return null;
1284: }
1285:
1286: static Set getMatchingImportedExportedKeys(Map objectKeys,
1287: Map associationKeys) {
1288: Set keys = new HashSet();
1289:
1290: for (Iterator it = associationKeys.entrySet().iterator(); it
1291: .hasNext();) {
1292: MetaData.Table.Key foreignKey = (MetaData.Table.Key) ((Map.Entry) it
1293: .next()).getValue();
1294:
1295: if (foreignKey != null) {
1296: MetaData.Table.Key localKey = (MetaData.Table.Key) objectKeys
1297: .get(foreignKey.getForeignColumnName());
1298:
1299: if (localKey != null)
1300: keys.add(foreignKey);
1301: }
1302: }
1303:
1304: return keys;
1305: }
1306:
1307: static Map getImportedExportedKeys(Database db, Object object,
1308: boolean exportedKeys) throws JPersistException,
1309: SQLException {
1310: Map keys = new HashMap();
1311: Class objectClass = object.getClass();
1312: Package p = objectClass.getPackage();
1313:
1314: while (objectClass != null
1315: && p == null
1316: || (p != null && (!objectClass.getPackage().equals(
1317: ObjectSupport.class.getPackage()) && !objectClass
1318: .getPackage().getName().equals("java.lang")))) {
1319: String tableName = getTableName(objectClass);
1320: MetaData.Table table = db.getMetaData().getTable(
1321: db.getConnection(), db.getTableMapper(),
1322: db.getColumnMapper(), db.getCatalogPattern(),
1323: db.getSchemaPattern(), tableName, object);
1324:
1325: if (table == null)
1326: throw new JPersistException("Table " + tableName
1327: + " is not locatable");
1328:
1329: if (exportedKeys)
1330: keys.putAll(table.getExportedKeys());
1331: else
1332: keys.putAll(table.getImportedKeys());
1333:
1334: objectClass = objectClass.getSuperclass();
1335: p = objectClass.getPackage();
1336: }
1337:
1338: return keys;
1339: }
1340:
1341: static long calculateChecksum(Database db, Object object)
1342: throws JPersistException, SQLException,
1343: IllegalAccessException, InvocationTargetException {
1344: ChecksumCalculator checksumCalculator = new ChecksumCalculator();
1345:
1346: processClasses(db, object.getClass(), object, true, false,
1347: false, false, null, checksumCalculator);
1348:
1349: return checksumCalculator.getCheckSum();
1350: }
1351:
1352: static class ChecksumCalculator implements ClassHandler {
1353: CRC32 checkSum = new CRC32();
1354:
1355: public void processClass(Class objectClass, Object object,
1356: MetaData.Table table, int numberTables,
1357: char tableAlias, Map valuesMap, Set selectableColumns)
1358: throws JPersistException, SQLException,
1359: IllegalAccessException, InvocationTargetException {
1360: for (Iterator it = valuesMap.entrySet().iterator(); it
1361: .hasNext();)
1362: checkSum.update(((Map.Entry) it.next()).getValue()
1363: .toString().getBytes());
1364: }
1365:
1366: long getCheckSum() {
1367: return checkSum.getValue();
1368: }
1369: }
1370:
1371: static interface ClassHandler {
1372: void processClass(Class objectClass, Object object,
1373: MetaData.Table table, int numberTables,
1374: char tableAlias, Map valuesMap, Set selectableColumns)
1375: throws JPersistException, SQLException,
1376: IllegalAccessException, InvocationTargetException;
1377: }
1378:
1379: static void processClasses(Database db, Class objectClass,
1380: Object object, boolean tableRequired,
1381: boolean IdColumnsOnly, boolean baseTableOnly,
1382: boolean isUpdate, Set nullValuesToInclude, ClassHandler ch)
1383: throws JPersistException, SQLException,
1384: IllegalAccessException, InvocationTargetException {
1385: Class headClass = objectClass, baseClass = null;
1386: Package p = objectClass.getPackage();
1387: char tableAlias = 'a';
1388: Vector classes = new Vector();
1389: boolean singleTable = objectClass
1390: .isAnnotationPresent(SingleTableInheritance.class), allFieldsSt = singleTable
1391: || objectClass
1392: .isAnnotationPresent(ConcreteTableInheritance.class);
1393: int numberOfTables = 0;
1394:
1395: while (p == null
1396: || (p != null && (!objectClass.getPackage().equals(
1397: ObjectSupport.class.getPackage()) && !objectClass
1398: .getPackage().getName().equals("java.lang")))) {
1399: classes.add(objectClass);
1400:
1401: baseClass = objectClass;
1402: objectClass = objectClass.getSuperclass();
1403:
1404: p = objectClass.getPackage();
1405: }
1406:
1407: if (baseTableOnly || allFieldsSt)
1408: numberOfTables = 1;
1409: else
1410: numberOfTables = classes.size();
1411:
1412: for (int i = classes.size() - 1; i > -1; i--, tableAlias++) {
1413: String tableName = null;
1414:
1415: if (allFieldsSt) {
1416: objectClass = headClass;
1417: tableName = getTableName(singleTable ? baseClass
1418: : headClass);
1419: } else {
1420: objectClass = (Class) classes.elementAt(i);
1421: tableName = getTableName(objectClass);
1422: }
1423:
1424: MetaData.Table table = db.getMetaData().getTable(
1425: db.getConnection(), db.getTableMapper(),
1426: db.getColumnMapper(), db.getCatalogPattern(),
1427: db.getSchemaPattern(), tableName, object);
1428:
1429: if (table == null && tableRequired)
1430: throw new JPersistException("Table " + tableName
1431: + " is not locatable");
1432:
1433: Map valuesMap = new HashMap();
1434: Set selectableColumns = new HashSet();
1435:
1436: if (table != null)
1437: getValuesMap(valuesMap, selectableColumns, table, db
1438: .getColumnMapper(), object, objectClass,
1439: nullValuesToInclude, IdColumnsOnly, isUpdate,
1440: allFieldsSt);
1441:
1442: ch.processClass(objectClass, object, table, numberOfTables,
1443: tableAlias, valuesMap, selectableColumns);
1444:
1445: if (baseTableOnly || allFieldsSt)
1446: break;
1447: }
1448: }
1449:
1450: static void getValuesMap(Map valuesMap, Set selectableColumns,
1451: MetaData.Table table, ColumnMapping columnMapper,
1452: Object object, Class objectClass, Set nullValuesToInclude,
1453: boolean IdColumnsOnly, boolean isUpdate,
1454: boolean allFieldsSti) throws IllegalAccessException,
1455: InvocationTargetException, JPersistException {
1456: Method methods[] = objectClass.getMethods();
1457:
1458: for (int i = 0; i < methods.length; i++) {
1459: if (methods[i].getName().startsWith("get")
1460: && (allFieldsSti || methods[i].getDeclaringClass()
1461: .equals(objectClass))
1462: && !methods[i].getName().equals("getDbAssociation")
1463: && methods[i].getParameterTypes().length == 0) {
1464: MetaData.Table.Column column = null;
1465: String methodName = methods[i].getName().substring(3);
1466: column = table.getColumn(columnMapper, methodName,
1467: object);
1468:
1469: if (column != null) {
1470: selectableColumns.add(column);
1471:
1472: if (object != null) {
1473: Object value = methods[i].invoke(object,
1474: (Object[]) null);
1475:
1476: if (IdColumnsOnly == false
1477: || table.getPrimaryKeys().size() == 0
1478: || column.isPrimaryKey()) {
1479: if (value != null)
1480: valuesMap.put(column, value);
1481: else {
1482: boolean includeNull = isUpdate
1483: && object != null
1484: && objectClass
1485: .isAnnotationPresent(UpdateNullValues.class);
1486:
1487: if (!includeNull
1488: && nullValuesToInclude != null)
1489: includeNull = nullValuesToInclude
1490: .contains(methodName)
1491: || nullValuesToInclude
1492: .contains(column
1493: .getClassName())
1494: || nullValuesToInclude
1495: .contains(Character
1496: .toLowerCase(methodName
1497: .charAt(0))
1498: + methodName
1499: .substring(1));
1500:
1501: if (includeNull)
1502: valuesMap.put(column,
1503: new NullValue(column
1504: .getDataType()));
1505: }
1506: }
1507: }
1508: }
1509: }
1510: }
1511: }
1512:
1513: static void processExternalClauses(
1514: StringBuffer externalClausesStrBuf, MetaData.Table table,
1515: ColumnMapping columnMapper, Object object,
1516: String identifierQuoteString) throws JPersistException {
1517: int pos = 0;
1518:
1519: while ((pos = externalClausesStrBuf.indexOf(":")) > -1) {
1520: int pos2 = 0;
1521:
1522: for (pos2 = pos + 1; pos2 < externalClausesStrBuf.length()
1523: && "~`!@#$%^&*()-=+\\|]}[{'\";:/?.>,< "
1524: .indexOf(externalClausesStrBuf.charAt(pos2)) == -1; pos2++)
1525: ;
1526:
1527: String searchStr = externalClausesStrBuf.substring(pos + 1,
1528: pos2), replacement = table.getColumn(columnMapper,
1529: searchStr, object).getColumnName();
1530:
1531: if (replacement != null)
1532: externalClausesStrBuf.replace(pos, pos2,
1533: identifierQuoteString + replacement
1534: + identifierQuoteString);
1535: }
1536: }
1537:
1538: static boolean valueIsZeroOrFalse(Object object) {
1539: if (object instanceof Integer)
1540: return ((Integer) object).intValue() == 0;
1541:
1542: if (object instanceof Short)
1543: return ((Short) object).shortValue() == 0;
1544:
1545: if (object instanceof Long)
1546: return ((Long) object).longValue() == 0;
1547:
1548: if (object instanceof Float)
1549: return ((Float) object).floatValue() == 0.0;
1550:
1551: if (object instanceof Double)
1552: return ((Double) object).doubleValue() == 0.0;
1553:
1554: if (object instanceof Boolean)
1555: return ((Boolean) object).booleanValue() == false;
1556:
1557: return false;
1558: }
1559:
1560: static String getTableName(Class objectClass) {
1561: String tableName = objectClass.getName().replace('$', '.');
1562:
1563: if (tableName.lastIndexOf('.') != -1)
1564: tableName = tableName
1565: .substring(tableName.lastIndexOf('.') + 1);
1566:
1567: if (tableName.indexOf(';') != -1)
1568: tableName = tableName.substring(0, tableName.indexOf(';'));
1569:
1570: return tableName;
1571: }
1572:
1573: static boolean hasWildCards(String value) {
1574: if (value.indexOf('%') != -1 || value.indexOf('_') != -1)
1575: return true;
1576:
1577: return false;
1578: }
1579:
1580: static class NullValue {
1581: int sqlType;
1582:
1583: NullValue(int sqlType) {
1584: this .sqlType = sqlType;
1585: }
1586:
1587: public int getSqlType() {
1588: return sqlType;
1589: }
1590: }
1591: }
|