0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/db/tags/sakai_2-4-1/db-util/storage/src/java/org/sakaiproject/util/BaseDbFlatStorage.java $
0003: * $Id: BaseDbFlatStorage.java 22826 2007-03-17 18:59:47Z ggolden@umich.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.util;
0021:
0022: // import
0023: import java.sql.Connection;
0024: import java.sql.ResultSet;
0025: import java.sql.SQLException;
0026: import java.util.Enumeration;
0027: import java.util.Hashtable;
0028: import java.util.Iterator;
0029: import java.util.List;
0030: import java.util.Properties;
0031: import java.util.Vector;
0032:
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035: import org.sakaiproject.db.api.SqlReader;
0036: import org.sakaiproject.db.api.SqlService;
0037: import org.sakaiproject.entity.api.Edit;
0038: import org.sakaiproject.entity.api.Entity;
0039: import org.sakaiproject.entity.api.ResourceProperties;
0040: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0041: import org.sakaiproject.event.cover.UsageSessionService;
0042: import org.sakaiproject.time.cover.TimeService;
0043:
0044: /**
0045: * <p>
0046: * BaseDbFlatStorage is a class that stores Resources (of some type) in a database, provides (optional) locked access, <br />
0047: * and generally implements a services "storage" class. <br />
0048: * The service's storage class can extend this to provide covers to turn Resource and Edit into something more type specific to the service.
0049: * </p>
0050: * <p>
0051: * Note: the methods here are all "id" based, with the following assumptions:
0052: * <ul>
0053: * <li>just the Resource Id field is enough to distinguish one Resource from another</li>
0054: * <li> a resource's reference is based on no more than the resource id</li>
0055: * <li> a resource's id cannot change</li>
0056: * </ul>
0057: * <br />
0058: * In order to handle Unicode characters properly, the SQL statements executed by this class should not embed Unicode characters into the SQL statement text; <br />
0059: * rather, Unicode values should be inserted as fields in a PreparedStatement. Databases handle Unicode better in fields.
0060: * </p>
0061: */
0062: public class BaseDbFlatStorage {
0063: /** Our logger. */
0064: private static Log M_log = LogFactory
0065: .getLog(BaseDbFlatStorage.class);
0066:
0067: /** Table name for resource records. */
0068: protected String m_resourceTableName = null;
0069:
0070: /** Table name for the resource properties. */
0071: protected String m_resourcePropertyTableName = null;
0072:
0073: /** The field in the resource table that holds the resource id. */
0074: protected String m_resourceTableIdField = null;
0075:
0076: /** The field in the resource table that is used for sorting (first sort). */
0077: protected String m_resourceTableSortField1 = null;
0078:
0079: /** The field in the resource table that is used for sorting (second sort). */
0080: protected String m_resourceTableSortField2 = null;
0081:
0082: /** The full set of fields in the table to read. */
0083: protected String[] m_resourceTableReadFields = null;
0084:
0085: /** The full set of fields in the table for update - can be field name only, or field=xxx expression. */
0086: protected String[] m_resourceTableUpdateFields = null;
0087:
0088: /** The full set of fields in the table for insert - just field (not counting a dbid field). */
0089: protected String[] m_resourceTableInsertFields = null;
0090:
0091: /** The full set of value expressions for an insert - either null or ? or an expression - to match m_resourceTableInsertFields (not counting a dbid field). */
0092: protected String[] m_resourceTableInsertValues = null;
0093:
0094: /** The extra db field for an integer 'db' id - auto-written on insert only. */
0095: protected String m_resourceTableDbidField = null;
0096:
0097: /** If false, we are not doing any locking, else we are. */
0098: protected boolean m_locking = true;
0099:
0100: /** If true, we do our locks in the remote database using a separate locking table, otherwise we do them in the class. */
0101: protected boolean m_locksAreInTable = true;
0102:
0103: /** Locks (if used), keyed by reference, holding Edits. */
0104: protected Hashtable m_locks = null;
0105:
0106: /** If set, we treat reasource ids as case insensitive. */
0107: protected boolean m_caseInsensitive = false;
0108:
0109: /** Injected (by constructor) SqlService. */
0110: protected SqlService m_sql = null;
0111:
0112: /** SqlReader to use when reading the record. */
0113: protected SqlReader m_reader = null;
0114:
0115: /**
0116: * Construct.
0117: *
0118: * @param resourceTableName
0119: * Table name for resources.
0120: * @param resourceTableIdField
0121: * The field in the resource table that holds the id.
0122: * @param resourceTableFields
0123: * The complete set of fields to read / write for the resource.
0124: * @param propertyTableName
0125: * The table name for standard property support.
0126: * @param locksInTable
0127: * If true, we do our locks in the remote database in a locks table, otherwise we do them here.
0128: * @param reader
0129: * A SqlReader which will produce Edits given fields read from the table.
0130: * @param sqlService
0131: * The SqlService.
0132: */
0133: public BaseDbFlatStorage(String resourceTableName,
0134: String resourceTableIdField, String[] resourceTableFields,
0135: String propertyTableName, boolean locksInTable,
0136: SqlReader reader, SqlService sqlService) {
0137: m_resourceTableName = resourceTableName;
0138: m_resourceTableIdField = resourceTableIdField;
0139: m_resourceTableSortField1 = resourceTableIdField;
0140: m_resourceTableReadFields = resourceTableFields;
0141: m_resourcePropertyTableName = propertyTableName;
0142: m_locksAreInTable = locksInTable;
0143: m_sql = sqlService;
0144: m_reader = reader;
0145:
0146: m_resourceTableUpdateFields = resourceTableFields;
0147: m_resourceTableInsertFields = resourceTableFields;
0148: m_resourceTableInsertValues = resourceTableFields;
0149: }
0150:
0151: /**
0152: * Set the sort field to be something perhaps other than the default of the id field.
0153: *
0154: * @param sortField1
0155: * The field name to use for sorting.
0156: * @param sortField2
0157: * Optional second sort field.
0158: */
0159: public void setSortField(String sortField1, String sortField2) {
0160: m_resourceTableSortField1 = sortField1;
0161: m_resourceTableSortField2 = sortField2;
0162: }
0163:
0164: /**
0165: * Set a field that will be read after the field list, and
0166: *
0167: * @param dbidField
0168: */
0169: public void setDbidField(String dbidField) {
0170: m_resourceTableDbidField = dbidField;
0171: }
0172:
0173: /**
0174: * Establish a different set of fields for inserts and updated.
0175: *
0176: * @param resourceTableFields
0177: * The complete set of fields to write for the resource (not counting a dbid field if needed).
0178: */
0179: public void setWriteFields(String[] updateFields,
0180: String[] insertFields, String[] insertValues) {
0181: m_resourceTableUpdateFields = updateFields;
0182: m_resourceTableInsertFields = insertFields;
0183: m_resourceTableInsertValues = insertValues;
0184: }
0185:
0186: /**
0187: * Set if we are doing locking or not.
0188: *
0189: * @param value
0190: * If true, we should do locking, else not.
0191: */
0192: public void setLocking(boolean value) {
0193: m_locking = value;
0194: }
0195:
0196: /**
0197: * Open and be ready to read / write.
0198: */
0199: public void open() {
0200: // setup for locks
0201: m_locks = new Hashtable();
0202: }
0203:
0204: /**
0205: * Close.
0206: */
0207: public void close() {
0208: if (!m_locks.isEmpty()) {
0209: M_log.warn("close(): locks remain!");
0210: // %%%
0211: }
0212: m_locks.clear();
0213: m_locks = null;
0214: }
0215:
0216: /**
0217: * Check if a Resource by this id exists.
0218: *
0219: * @param id
0220: * The id.
0221: * @return true if a Resource by this id exists, false if not.
0222: */
0223: public boolean checkResource(String id) {
0224: // just see if the record exists
0225: String sql = "select " + m_resourceTableIdField + " from "
0226: + m_resourceTableName + " where ( "
0227: + m_resourceTableIdField + " = ? )";
0228:
0229: Object fields[] = new Object[1];
0230: fields[0] = caseId(id);
0231: List ids = m_sql.dbRead(sql, fields, null);
0232:
0233: return (!ids.isEmpty());
0234: }
0235:
0236: /**
0237: * Get the Resource with this id, or null if not found.
0238: *
0239: * @param id
0240: * The id.
0241: * @return The Resource with this id, or null if not found.
0242: */
0243: public Entity getResource(String id) {
0244: return getResource(null, id);
0245: }
0246:
0247: /**
0248: * Get the Resource with this id, or null if not found.
0249: *
0250: * @param optional
0251: * connection to use.
0252: * @param id
0253: * The id.
0254: * @return The Resource with this id, or null if not found.
0255: */
0256: public Entity getResource(Connection conn, String id) {
0257: Entity entry = null;
0258:
0259: // get the user from the db
0260: String sql = "select "
0261: + fieldList(m_resourceTableReadFields, null) + " from "
0262: + m_resourceTableName + " where ( "
0263: + m_resourceTableIdField + " = ? )";
0264:
0265: Object fields[] = new Object[1];
0266: fields[0] = caseId(id);
0267: List rv = m_sql.dbRead(conn, sql, fields, m_reader);
0268:
0269: if ((rv != null) && (rv.size() > 0)) {
0270: entry = (Entity) rv.get(0);
0271: }
0272:
0273: return entry;
0274: }
0275:
0276: public List getAllResources() {
0277: // read all resources from the db
0278: String sql = "select "
0279: + fieldList(m_resourceTableReadFields, null) + " from "
0280: + m_resourceTableName;
0281:
0282: List rv = m_sql.dbRead(sql, null, m_reader);
0283:
0284: return rv;
0285: }
0286:
0287: public int countAllResources() {
0288: List all = new Vector();
0289:
0290: // read all count
0291: String sql = "select count(1) from " + m_resourceTableName;
0292:
0293: List results = m_sql.dbRead(sql, null, new SqlReader() {
0294: public Object readSqlResultRecord(ResultSet result) {
0295: try {
0296: int count = result.getInt(1);
0297: return new Integer(count);
0298: } catch (SQLException ignore) {
0299: return null;
0300: }
0301: }
0302: });
0303:
0304: if (results.isEmpty())
0305: return 0;
0306:
0307: return ((Integer) results.get(0)).intValue();
0308: }
0309:
0310: public List getAllResources(int first, int last) {
0311: String sql;
0312: Object[] fields = null;
0313: if ("oracle".equals(m_sql.getVendor())) {
0314: // use Oracle RANK function, adding the id to the sort fields to assure we have a unique ranking
0315: sql = "select "
0316: + /* fieldList(m_resourceTableReadFields, null) */"*"
0317: + " from" + " (select "
0318: + fieldList(m_resourceTableReadFields, null)
0319: + " ,RANK() OVER"
0320: + " (order by "
0321: + m_resourceTableName
0322: + "."
0323: + m_resourceTableSortField1
0324: + (m_resourceTableSortField2 == null ? "" : ","
0325: + m_resourceTableName + "."
0326: + m_resourceTableSortField2)
0327: + ","
0328: + m_resourceTableName
0329: + "."
0330: + m_resourceTableIdField
0331: + ") as rank"
0332: + " from "
0333: + m_resourceTableName
0334: + " order by "
0335: + m_resourceTableName
0336: + "."
0337: + m_resourceTableSortField1
0338: + (m_resourceTableSortField2 == null ? "" : ","
0339: + m_resourceTableName + "."
0340: + m_resourceTableSortField2)
0341: + ","
0342: + m_resourceTableName
0343: + "."
0344: + m_resourceTableIdField
0345: + " )"
0346: + " where rank between ? and ?";
0347: fields = new Object[2];
0348: fields[0] = new Long(first);
0349: fields[1] = new Long(last);
0350: } else if ("mysql".equals(m_sql.getVendor())) {
0351: // use MySQL LIMIT clause
0352: sql = "select "
0353: + fieldList(m_resourceTableReadFields, null)
0354: + " from "
0355: + m_resourceTableName
0356: + " order by "
0357: + m_resourceTableName
0358: + "."
0359: + m_resourceTableSortField1
0360: + (m_resourceTableSortField2 == null ? "" : ","
0361: + m_resourceTableName + "."
0362: + m_resourceTableSortField2) + " limit "
0363: + (last - first + 1) + " offset " + (first - 1);
0364: } else
0365: // if ("hsqldb".equals(m_sql.getVendor()))
0366: {
0367: // use SQL2000 clause
0368: sql = "select "
0369: + "limit "
0370: + (first - 1)
0371: + " "
0372: + (last - first + 1)
0373: + " "
0374: + fieldList(m_resourceTableReadFields, null)
0375: + " from "
0376: + m_resourceTableName
0377: + " order by "
0378: + m_resourceTableName
0379: + "."
0380: + m_resourceTableSortField1
0381: + (m_resourceTableSortField2 == null ? "" : ","
0382: + m_resourceTableName + "."
0383: + m_resourceTableSortField2);
0384: }
0385:
0386: List rv = m_sql.dbRead(sql, fields, m_reader);
0387:
0388: return rv;
0389: }
0390:
0391: /**
0392: * Get all Resources matching a SQL where clause, with sorting
0393: *
0394: * @param where
0395: * The SQL where clause with bind variables indicated (not including the preceeding "where ").
0396: * @param values
0397: * The bind values
0398: * @return The list of all Resources that meet the criteria.
0399: */
0400: public List getSelectedResources(String where, Object[] values) {
0401: return getSelectedResources(where, null, values, null);
0402: }
0403:
0404: /**
0405: * Get all Resources matching a SQL where clause, with sorting and ordering
0406: *
0407: * @param where
0408: * The SQL where clause with bind variables indicated (not including the preceeding "where ").
0409: * @param order
0410: * the SQL order clause (not including the preceeding "order by ").
0411: * @param values
0412: * The bind values
0413: * @return The list of all Resources that meet the criteria.
0414: */
0415: public List getSelectedResources(String where, String order,
0416: Object[] values) {
0417: return getSelectedResources(where, order, values, null);
0418: }
0419:
0420: /**
0421: * Get all Resources matching a SQL where clause, with sorting and ordering
0422: *
0423: * @param where
0424: * The SQL where clause with bind variables indicated (not including the preceeding "where ").
0425: * @param order
0426: * the SQL order clause (not including the preceeding "order by ").
0427: * @param values
0428: * The bind values
0429: * @param join
0430: * a single or comma separated set of other tables to join in the from clause
0431: * @return The list of all Resources that meet the criteria.
0432: */
0433: public List getSelectedResources(String where, String order,
0434: Object[] values, String join) {
0435: if (order == null) {
0436: order = m_resourceTableName
0437: + "."
0438: + m_resourceTableSortField1
0439: + (m_resourceTableSortField2 == null ? "" : ","
0440: + m_resourceTableName + "."
0441: + m_resourceTableSortField2);
0442: }
0443: if (where == null)
0444: where = "";
0445:
0446: // read all resources from the db with a where
0447: String sql = "select "
0448: + fieldList(m_resourceTableReadFields, null) + " from "
0449: + m_resourceTableName
0450: + ((join == null) ? "" : ("," + join))
0451: + ((where.length() > 0) ? (" where " + where) : "")
0452: + " order by " + order;
0453:
0454: List all = m_sql.dbRead(sql, values, m_reader);
0455:
0456: return all;
0457: }
0458:
0459: /**
0460: * Count all Resources matching a SQL where clause.
0461: *
0462: * @param where
0463: * The SQL where clause with bind variables indicated (not including the preceeding "where ").
0464: * @param values
0465: * The bind values
0466: * @return The count of all Resources that meet the criteria.
0467: */
0468: public int countSelectedResources(String where, Object[] values) {
0469: return countSelectedResources(where, values, null);
0470: }
0471:
0472: /**
0473: * Count all Resources matching a SQL where clause.
0474: *
0475: * @param where
0476: * The SQL where clause with bind variables indicated (not including the preceeding "where ").
0477: * @param values
0478: * The bind values
0479: * @param join
0480: * a single or comma separated set of other tables to join in the from clause
0481: * @return The count of all Resources that meet the criteria.
0482: */
0483: public int countSelectedResources(String where, Object[] values,
0484: String join) {
0485: // read all resources from the db with a where
0486: String sql = "select count(1) from "
0487: + m_resourceTableName
0488: + ((join == null) ? "" : ("," + join))
0489: + (((where != null) && (where.length() > 0)) ? (" where " + where)
0490: : "");
0491:
0492: List results = m_sql.dbRead(sql, values, new SqlReader() {
0493: public Object readSqlResultRecord(ResultSet result) {
0494: try {
0495: int count = result.getInt(1);
0496: return new Integer(count);
0497: } catch (SQLException ignore) {
0498: return null;
0499: }
0500: }
0501: });
0502:
0503: if (results.isEmpty())
0504: return 0;
0505:
0506: return ((Integer) results.get(0)).intValue();
0507: }
0508:
0509: /**
0510: * Get all Resources matching a SQL where clause.
0511: *
0512: * @param where
0513: * The SQL where clause with bind variables indicated (not including the preceeding "where ".
0514: * @param values
0515: * The bind values
0516: * @return The list of all Resources that meet the criteria.
0517: */
0518: public List getSelectedResources(String where, Object[] values,
0519: int first, int last) {
0520: return getSelectedResources(where, null, values, first, last,
0521: null);
0522: }
0523:
0524: /**
0525: * Get all Resources matching a SQL where clause.
0526: *
0527: * @param where
0528: * The SQL where clause with bind variables indicated (not including the preceeding "where ".
0529: * @param order
0530: * the SQL order clause (not including the preceeding "order by ").
0531: * @param values
0532: * The bind values
0533: * @return The list of all Resources that meet the criteria.
0534: */
0535: public List getSelectedResources(String where, String order,
0536: Object[] values, int first, int last) {
0537: return getSelectedResources(where, order, values, first, last,
0538: null);
0539: }
0540:
0541: /**
0542: * Get all Resources matching a SQL where clause.
0543: *
0544: * @param where
0545: * The SQL where clause with bind variables indicated (not including the preceeding "where ".
0546: * @param order
0547: * the SQL order clause (not including the preceeding "order by ").
0548: * @param values
0549: * The bind values
0550: * @param join
0551: * a single or comma separated set of other tables to join in the from clause
0552: * @return The list of all Resources that meet the criteria.
0553: */
0554: public List getSelectedResources(String where, String order,
0555: Object[] values, int first, int last, String join) {
0556: Object[] fields;
0557: String sql;
0558:
0559: if (order == null) {
0560: order = m_resourceTableName
0561: + "."
0562: + m_resourceTableSortField1
0563: + (m_resourceTableSortField2 == null ? "" : ","
0564: + m_resourceTableName + "."
0565: + m_resourceTableSortField2);
0566: }
0567:
0568: if ("oracle".equals(m_sql.getVendor())) {
0569: if (values != null) {
0570: fields = new Object[2 + values.length];
0571: System.arraycopy(values, 0, fields, 0, values.length);
0572: } else {
0573: fields = new Object[2];
0574: }
0575:
0576: // use Oracle RANK function, adding the id field to the order to assure a unique ranking
0577: sql = "select "
0578: + /* fieldList(m_resourceTableReadFields, null) */"*"
0579: + " from"
0580: + " (select "
0581: + fieldList(m_resourceTableReadFields, null)
0582: + " ,RANK() OVER"
0583: + " (order by "
0584: + order
0585: + ","
0586: + m_resourceTableName
0587: + "."
0588: + m_resourceTableIdField
0589: + ") as rank"
0590: + " from "
0591: + m_resourceTableName
0592: + ((join == null) ? "" : ("," + join))
0593: + (((where != null) && (where.length() > 0)) ? (" where " + where)
0594: : "") + " order by " + order + ","
0595: + m_resourceTableName + "."
0596: + m_resourceTableIdField + " )"
0597: + " where rank between ? and ?";
0598: fields[fields.length - 2] = new Long(first);
0599: fields[fields.length - 1] = new Long(last);
0600: } else if ("mysql".equals(m_sql.getVendor())) {
0601: fields = values;
0602: // use MySQL LIMIT clause
0603: sql = "select "
0604: + fieldList(m_resourceTableReadFields, null)
0605: + " from "
0606: + m_resourceTableName
0607: + ((join == null) ? "" : ("," + join))
0608: + (((where != null) && (where.length() > 0)) ? (" where " + where)
0609: : "")
0610: + " order by "
0611: + order
0612: + ","
0613: + m_resourceTableName
0614: + "."
0615: + m_resourceTableSortField1
0616: + (m_resourceTableSortField2 == null ? "" : ","
0617: + m_resourceTableName + "."
0618: + m_resourceTableSortField2) + " limit "
0619: + (last - first + 1) + " offset " + (first - 1);
0620: } else
0621: // if ("hsqldb".equals(m_sql.getVendor()))
0622: {
0623: // use SQL2000 LIMIT clause
0624: fields = values;
0625: sql = "select "
0626: + "limit "
0627: + (first - 1)
0628: + " "
0629: + (last - first + 1)
0630: + " "
0631: + fieldList(m_resourceTableReadFields, null)
0632: + " from "
0633: + m_resourceTableName
0634: + ((join == null) ? "" : ("," + join))
0635: + (((where != null) && (where.length() > 0)) ? (" where " + where)
0636: : "")
0637: + " order by "
0638: + order
0639: + ","
0640: + m_resourceTableName
0641: + "."
0642: + m_resourceTableSortField1
0643: + (m_resourceTableSortField2 == null ? "" : ","
0644: + m_resourceTableName + "."
0645: + m_resourceTableSortField2);
0646: }
0647:
0648: List rv = m_sql.dbRead(sql, fields, m_reader);
0649:
0650: return rv;
0651: }
0652:
0653: /**
0654: * Add a new Resource with this id.
0655: *
0656: * @param id
0657: * The id.
0658: * @param fields
0659: * The fields to write.
0660: * @return The locked Resource object with this id, or null if the id is in use.
0661: */
0662: public Edit putResource(String id, Object[] fields) {
0663: return putResource(null, id, fields);
0664: }
0665:
0666: /**
0667: * putResource with optional connection to use.
0668: *
0669: * @param conn
0670: * The optional database connection to use.
0671: * @param id
0672: * The id.
0673: * @param fields
0674: * The fields to write.
0675: * @return The locked Resource object with this id, or null if the id is in use.
0676: */
0677: public Edit putResource(Connection conn, String id, Object[] fields) {
0678: // process the insert
0679: boolean ok = insertResource(id, fields, conn);
0680:
0681: // if this failed, assume a key conflict (i.e. id in use)
0682: if (!ok)
0683: return null;
0684:
0685: // now get a lock on the record for edit
0686: Edit edit = editResource(conn, id);
0687: if (edit == null) {
0688: M_log.warn("putResource(): didn't get a lock!");
0689: return null;
0690: }
0691:
0692: return edit;
0693: }
0694:
0695: /**
0696: * Add a new Resource with this id - no edit is returned, no lock is held.
0697: *
0698: * @param id
0699: * The id.
0700: * @param fields
0701: * The fields to write.
0702: * @return True if successful, false if not.
0703: */
0704: public boolean insertResource(String id, Object[] fields,
0705: Connection conn) {
0706: String statement = "insert into "
0707: + m_resourceTableName
0708: + "( "
0709: + fieldList(m_resourceTableInsertFields,
0710: m_resourceTableDbidField)
0711: + " )"
0712: + " values ( "
0713: + valuesParams(m_resourceTableInsertValues,
0714: (m_resourceTableDbidField)) + " )";
0715:
0716: // process the insert
0717: boolean ok = m_sql.dbWrite(conn, statement, fields);
0718: return ok;
0719:
0720: } // putResource
0721:
0722: /**
0723: * Get a lock on the Resource with this id, or null if a lock cannot be gotten.
0724: *
0725: * @param id
0726: * The user id.
0727: * @return The locked Resource with this id, or null if this records cannot be locked.
0728: */
0729: public Edit editResource(String id) {
0730: return editResource(null, id);
0731: }
0732:
0733: /**
0734: * Get a lock on the Resource with this id, or null if a lock cannot be gotten.
0735: *
0736: * @param conn
0737: * The optional database connection to use.
0738: * @param id
0739: * The user id.
0740: * @return The locked Resource with this id, or null if this records cannot be locked.
0741: */
0742: public Edit editResource(Connection conn, String id) {
0743: Edit edit = null;
0744:
0745: if (!m_locking) {
0746: return (Edit) getResource(conn, id);
0747: }
0748:
0749: // if the locks are in a separate table in the db
0750: if (m_locksAreInTable) {
0751: // read the record - fail if not there
0752: Entity entry = getResource(conn, id);
0753: if (entry == null)
0754: return null;
0755:
0756: // write a lock to the lock table - if we can do it, we get the lock
0757: String statement = "insert into SAKAI_LOCKS"
0758: + " (TABLE_NAME,RECORD_ID,LOCK_TIME,USAGE_SESSION_ID)"
0759: + " values (?, ?, ?, ?)";
0760:
0761: // we need session id and user id
0762: String sessionId = UsageSessionService.getSessionId();
0763: if (sessionId == null) {
0764: sessionId = "";
0765: }
0766:
0767: // collect the fields
0768: Object fields[] = new Object[4];
0769: fields[0] = m_resourceTableName;
0770: fields[1] = internalRecordId(caseId(id));
0771: fields[2] = TimeService.newTime();
0772: fields[3] = sessionId;
0773:
0774: // add the lock - if fails, someone else has the lock
0775: boolean ok = m_sql
0776: .dbWriteFailQuiet(null, statement, fields);
0777: if (!ok) {
0778: return null;
0779: }
0780:
0781: // we got the lock! - make the edit from the Resource
0782: edit = (Edit) entry;
0783: }
0784:
0785: // otherwise, get the lock locally
0786: else {
0787: // get the entry, and check for existence
0788: Entity entry = getResource(conn, id);
0789: if (entry == null)
0790: return null;
0791:
0792: // we only sync this getting - someone may release a lock out of sync
0793: synchronized (m_locks) {
0794: // if already locked
0795: if (m_locks.containsKey(entry.getReference()))
0796: return null;
0797:
0798: // make the edit from the Resource
0799: edit = (Edit) entry;
0800:
0801: // store the edit in the locks by reference
0802: m_locks.put(entry.getReference(), edit);
0803: }
0804: }
0805:
0806: return edit;
0807: }
0808:
0809: /**
0810: * Commit the changes and release the lock.
0811: *
0812: * @param edit
0813: * The Edit to commit.
0814: * @param fields
0815: * The set of fields to write to the db, plus the id field as it is to be written again at the end.
0816: */
0817: public void commitResource(Edit edit, Object fields[],
0818: ResourceProperties props) {
0819: commitResource(edit, fields, props, null);
0820: }
0821:
0822: /**
0823: * Commit the changes and release the lock - optionally in a transaction.
0824: *
0825: * @param edit
0826: * The Edit to commit.
0827: * @param fields
0828: * The set of fields to write to the db, plus the id field as it is to be written again at the end.
0829: * @param key
0830: * The object key used to relate to the properties - if null, we use the object id to relate.
0831: */
0832: public void commitResource(Edit edit, Object fields[],
0833: ResourceProperties props, Object key) {
0834: // write out the properties
0835: writeProperties(edit, props, key);
0836:
0837: String statement = "update " + m_resourceTableName + " set "
0838: + updateSet(m_resourceTableUpdateFields) + " where ( "
0839: + m_resourceTableIdField + " = ? )";
0840:
0841: // process the update
0842: m_sql.dbWrite(statement, updateFields(fields));
0843:
0844: if (m_locking) {
0845: if (m_locksAreInTable) {
0846: // remove the lock
0847: statement = "delete from SAKAI_LOCKS where TABLE_NAME = ? and RECORD_ID = ?";
0848:
0849: // collect the fields
0850: Object lockFields[] = new Object[2];
0851: lockFields[0] = m_resourceTableName;
0852: lockFields[1] = internalRecordId(caseId(edit.getId()));
0853: boolean ok = m_sql.dbWrite(statement, lockFields);
0854: if (!ok) {
0855: M_log.warn("commit: missing lock for table: "
0856: + lockFields[0] + " key: " + lockFields[1]);
0857: }
0858: }
0859:
0860: else {
0861: // remove the lock
0862: m_locks.remove(edit.getReference());
0863: }
0864: }
0865: }
0866:
0867: /**
0868: * Cancel the changes and release the lock.
0869: *
0870: * @param user
0871: * The Edit to cancel.
0872: */
0873: public void cancelResource(Edit edit) {
0874: if (m_locking) {
0875: if (m_locksAreInTable) {
0876: // remove the lock
0877: String statement = "delete from SAKAI_LOCKS where TABLE_NAME = ? and RECORD_ID = ?";
0878:
0879: // collect the fields
0880: Object lockFields[] = new Object[2];
0881: lockFields[0] = m_resourceTableName;
0882: lockFields[1] = internalRecordId(caseId(edit.getId()));
0883: boolean ok = m_sql.dbWrite(statement, lockFields);
0884: if (!ok) {
0885: M_log.warn("cancel: missing lock for table: "
0886: + lockFields[0] + " key: " + lockFields[1]);
0887: }
0888: }
0889:
0890: else {
0891: // release the lock
0892: m_locks.remove(edit.getReference());
0893: }
0894: }
0895: }
0896:
0897: /**
0898: * Remove this (locked) Resource.
0899: *
0900: * @param user
0901: * The Edit to remove.
0902: */
0903: public void removeResource(Edit edit) {
0904: removeResource(edit, null);
0905: }
0906:
0907: /**
0908: * Remove this (locked) Resource.
0909: *
0910: * @param edit
0911: * The Edit to remove.
0912: * @param key
0913: * The key to relate resource to properties, of if null, id is assumed.
0914: */
0915: public void removeResource(final Edit edit, final Object key) {
0916: // do this in a transaction
0917: m_sql.transact(new Runnable() {
0918: public void run() {
0919: removeResourceTx(edit, key);
0920: }
0921: }, "removeResource:" + edit.getId());
0922: }
0923:
0924: /**
0925: * Transaction code to remove a resource.
0926: */
0927: protected void removeResourceTx(Edit edit, Object key) {
0928: // remove the properties
0929: deleteProperties(edit, key);
0930:
0931: // form the SQL delete statement
0932: String statement = "delete from " + m_resourceTableName
0933: + " where ( " + m_resourceTableIdField + " = ? )";
0934:
0935: Object fields[] = new Object[1];
0936: fields[0] = caseId(edit.getId());
0937:
0938: // process the delete statement
0939: m_sql.dbWrite(statement, fields);
0940:
0941: if (m_locking) {
0942: if (m_locksAreInTable) {
0943: // remove the lock
0944: statement = "delete from SAKAI_LOCKS where TABLE_NAME = ? and RECORD_ID = ?";
0945:
0946: // collect the fields
0947: Object lockFields[] = new Object[2];
0948: lockFields[0] = m_resourceTableName;
0949: lockFields[1] = internalRecordId(caseId(edit.getId()));
0950: boolean ok = m_sql.dbWrite(statement, lockFields);
0951: if (!ok) {
0952: M_log.warn("remove: missing lock for table: "
0953: + lockFields[0] + " key: " + lockFields[1]);
0954: }
0955: }
0956:
0957: else {
0958: // release the lock
0959: m_locks.remove(edit.getReference());
0960: }
0961: }
0962: }
0963:
0964: /**
0965: * Read in properties from the database - when the properties and the main table are related by the id
0966: *
0967: * @param r
0968: * The resource for which properties are to be read.
0969: * @param p
0970: * The properties object to fill.
0971: */
0972: public void readProperties(Entity r, ResourcePropertiesEdit p) {
0973: readProperties(null, m_resourcePropertyTableName,
0974: m_resourceTableIdField, caseId(r.getId()), p);
0975: }
0976:
0977: /**
0978: * Read in properties from the database - when the properties and the main table are related by the dbid
0979: *
0980: * @param key
0981: * The resource key.
0982: * @param p
0983: * The properties object to fill.
0984: */
0985: public void readProperties(Integer dbid, ResourcePropertiesEdit p) {
0986: readProperties(null, m_resourcePropertyTableName,
0987: m_resourceTableDbidField, dbid, p);
0988: }
0989:
0990: /**
0991: * Read in properties from the database - when the properties and the main table are related by the id
0992: *
0993: * @param r
0994: * The resource for which properties are to be read.
0995: * @param p
0996: * The properties object to fill.
0997: */
0998: public void readProperties(Connection conn, Entity r,
0999: ResourcePropertiesEdit p) {
1000: readProperties(conn, m_resourcePropertyTableName,
1001: m_resourceTableIdField, caseId(r.getId()), p);
1002: }
1003:
1004: /**
1005: * Read in properties from the database - when the properties and the main table are related by the dbid
1006: *
1007: * @param key
1008: * The resource key.
1009: * @param p
1010: * The properties object to fill.
1011: */
1012: public void readProperties(Connection conn, Integer dbid,
1013: ResourcePropertiesEdit p) {
1014: readProperties(conn, m_resourcePropertyTableName,
1015: m_resourceTableDbidField, dbid, p);
1016: }
1017:
1018: /**
1019: * Read in properties from the database.
1020: *
1021: * @param r
1022: * The resource for which properties are to be read.
1023: */
1024: public void readProperties(Connection conn, String table,
1025: String idField, Object id, ResourcePropertiesEdit p) {
1026: // if not properties table set, skip it
1027: if (table == null)
1028: return;
1029:
1030: // the properties to fill in
1031: final ResourcePropertiesEdit props = p;
1032:
1033: // get the properties from the db
1034: // ASSUME: NAME, VALUE for fields
1035: String sql = "select NAME, VALUE from " + table + " where ( "
1036: + idField + " = ? )";
1037:
1038: Object fields[] = new Object[1];
1039: fields[0] = id;
1040: m_sql.dbRead(conn, sql, fields, new SqlReader() {
1041: public Object readSqlResultRecord(ResultSet result) {
1042: try {
1043: // read the fields
1044: String name = result.getString(1);
1045: String value = result.getString(2);
1046:
1047: // add to props, if we got stuff from the fields
1048: if ((name != null) && (value != null)) {
1049: props.addProperty(name, value);
1050: }
1051:
1052: // nothing to return
1053: return null;
1054: } catch (SQLException e) {
1055: M_log.warn("readProperties: " + e);
1056: return null;
1057: }
1058: }
1059: });
1060: }
1061:
1062: /**
1063: * Read in properties from the database.
1064: *
1065: * @param r
1066: * The resource for which properties are to be read.
1067: */
1068: public void readProperties(Connection conn, String table,
1069: String idField, Object id, Properties p) {
1070: // if not properties table set, skip it
1071: if (table == null)
1072: return;
1073:
1074: // the properties to fill in
1075: final Properties props = p;
1076:
1077: // get the properties from the db
1078: // ASSUME: NAME, VALUE for fields
1079: String sql = "select NAME, VALUE from " + table + " where ( "
1080: + idField + " = ? )";
1081:
1082: Object fields[] = new Object[1];
1083: fields[0] = id;
1084: m_sql.dbRead(conn, sql, fields, new SqlReader() {
1085: public Object readSqlResultRecord(ResultSet result) {
1086: try {
1087: // read the fields
1088: String name = result.getString(1);
1089: String value = result.getString(2);
1090:
1091: // add to props, if we got stuff from the fields
1092: if ((name != null) && (value != null)) {
1093: props.setProperty(name, value);
1094: }
1095:
1096: // nothing to return
1097: return null;
1098: } catch (SQLException e) {
1099: M_log.warn("readProperties: " + e);
1100: return null;
1101: }
1102: }
1103: });
1104: }
1105:
1106: /**
1107: * Replace any properties for this resource with the resource's current set of properties.
1108: *
1109: * @param conn
1110: * optional database connection to use.
1111: * @param r
1112: * The resource for which properties are to be written.
1113: * @param props
1114: * The properties to write.
1115: */
1116: public void writeProperties(Entity r, ResourceProperties props) {
1117: writeProperties(m_resourcePropertyTableName,
1118: m_resourceTableIdField, caseId(r.getId()), null, null,
1119: props);
1120: }
1121:
1122: /**
1123: * Replace any properties for this resource with the resource's current set of properties.
1124: *
1125: * @param conn
1126: * optional database connection to use.
1127: * @param r
1128: * The resource for which properties are to be written.
1129: * @param props
1130: * The properties to write.
1131: * @param key
1132: * The key used to relate the props to the resource.
1133: */
1134: public void writeProperties(Entity r, ResourceProperties props,
1135: Object key) {
1136: if (key == null) {
1137: writeProperties(m_resourcePropertyTableName,
1138: m_resourceTableIdField, caseId(r.getId()), null,
1139: null, props);
1140: } else {
1141: writeProperties(m_resourcePropertyTableName,
1142: m_resourceTableDbidField, key, null, null, props);
1143: }
1144: }
1145:
1146: public void writeProperties(String table, String idField,
1147: Object id, String extraIdField, String extraId,
1148: ResourceProperties props) {
1149: boolean deleteFirst = true;
1150: writeProperties(table, idField, id, extraIdField, extraId,
1151: props, deleteFirst);
1152: }
1153:
1154: /**
1155: * Replace any properties for this resource with the resource's current set of properties.
1156: *
1157: * @param r
1158: * The resource for which properties are to be written.
1159: */
1160: public void writeProperties(final String table,
1161: final String idField, final Object id,
1162: final String extraIdField, final String extraId,
1163: final ResourceProperties props, final boolean deleteFirst) {
1164: // if not properties table set, skip it
1165: if (table == null)
1166: return;
1167: if (props == null)
1168: return;
1169:
1170: // do this in a transaction
1171: m_sql.transact(new Runnable() {
1172: public void run() {
1173: writePropertiesTx(table, idField, id, extraIdField,
1174: extraId, props, deleteFirst);
1175: }
1176: }, "writeProperties:" + id);
1177: }
1178:
1179: /**
1180: * The transaction code that writes the properties.
1181: *
1182: * @param r
1183: * The resource for which properties are to be written.
1184: */
1185: protected void writePropertiesTx(String table, String idField,
1186: Object id, String extraIdField, String extraId,
1187: ResourceProperties props, boolean deleteFirst) {
1188: String statement;
1189: Object fields[];
1190:
1191: // if (true)
1192: if (deleteFirst) {
1193: // delete what's there
1194: statement = "delete from " + table + " where ( " + idField
1195: + " = ? )";
1196:
1197: fields = new Object[1];
1198: fields[0] = id;
1199:
1200: // process the delete statement
1201: m_sql.dbWrite(statement, fields);
1202: }
1203:
1204: // the SQL statement
1205: statement = "insert into " + table + "( " + idField
1206: + ", NAME, VALUE"
1207: + ((extraIdField != null) ? (", " + extraIdField) : "")
1208: + " ) values (?,?,?"
1209: + ((extraIdField != null) ? ",?" : "") + ")";
1210:
1211: fields = new Object[((extraIdField != null) ? 4 : 3)];
1212: fields[0] = id;
1213:
1214: // process each property
1215: for (Iterator i = props.getPropertyNames(); i.hasNext();) {
1216: String name = (String) i.next();
1217: String value = props.getProperty(name);
1218:
1219: fields[1] = name;
1220: fields[2] = value;
1221:
1222: if (extraIdField != null) {
1223: fields[3] = extraId;
1224: }
1225:
1226: // dont write it if there's only an empty string for value
1227: if (value.length() > 0) {
1228: m_sql.dbWrite(statement, fields);
1229: }
1230: }
1231: }
1232:
1233: /**
1234: * Replace any properties for this resource with the resource's current set of properties.
1235: *
1236: * @param r
1237: * The resource for which properties are to be written.
1238: */
1239: public void writeProperties(String table, String idField,
1240: Object id, String extraIdField, String extraId,
1241: Properties props) {
1242: boolean deleteFirst = true;
1243: writeProperties(table, idField, id, extraIdField, extraId,
1244: props, deleteFirst);
1245: }
1246:
1247: /**
1248: * Replace any properties for this resource with the resource's current set of properties.
1249: *
1250: * @param r
1251: * The resource for which properties are to be written.
1252: */
1253: public void writeProperties(final String table,
1254: final String idField, final Object id,
1255: final String extraIdField, final String extraId,
1256: final Properties props, final boolean deleteFirst) {
1257: // if not properties table set, skip it
1258: if (table == null)
1259: return;
1260: if (props == null)
1261: return;
1262:
1263: // do this in a transaction
1264: m_sql.transact(new Runnable() {
1265: public void run() {
1266: writePropertiesTx(table, idField, id, extraIdField,
1267: extraId, props, deleteFirst);
1268: }
1269: }, "writeProperties:" + id);
1270:
1271: }
1272:
1273: /**
1274: * The transaction code for writing properties.
1275: *
1276: * @param r
1277: * The resource for which properties are to be written.
1278: */
1279: protected void writePropertiesTx(String table, String idField,
1280: Object id, String extraIdField, String extraId,
1281: Properties props, boolean deleteFirst) {
1282: String statement;
1283: Object[] fields;
1284:
1285: if (deleteFirst) {
1286: // delete what's there
1287: statement = "delete from " + table + " where ( " + idField
1288: + " = ? )";
1289:
1290: fields = new Object[1];
1291: fields[0] = id;
1292:
1293: // process the delete statement
1294: m_sql.dbWrite(statement, fields);
1295: }
1296:
1297: // the SQL statement
1298: statement = "insert into " + table + "( " + idField
1299: + ", NAME, VALUE"
1300: + ((extraIdField != null) ? (", " + extraIdField) : "")
1301: + " ) values (?,?,?"
1302: + ((extraIdField != null) ? ",?" : "") + ")";
1303:
1304: fields = new Object[((extraIdField != null) ? 4 : 3)];
1305: fields[0] = id;
1306:
1307: // process each property
1308: for (Enumeration i = props.propertyNames(); i.hasMoreElements();) {
1309: String name = (String) i.nextElement();
1310: String value = props.getProperty(name);
1311:
1312: fields[1] = name;
1313: fields[2] = value;
1314:
1315: if (extraIdField != null) {
1316: fields[3] = extraId;
1317: }
1318:
1319: // dont write it if there's only an empty string for value
1320: if (value.length() > 0) {
1321: m_sql.dbWrite(statement, fields);
1322: }
1323: }
1324: }
1325:
1326: /**
1327: * Remove all properties for this resource from the db.
1328: *
1329: * @param r
1330: * The resource for which properties are to be deleted.
1331: */
1332: protected void deleteProperties(Entity r, Object key) {
1333: String idField = m_resourceTableIdField;
1334: if (key != null) {
1335: idField = m_resourceTableDbidField;
1336: }
1337:
1338: // if not properties table set, skip it
1339: if (m_resourcePropertyTableName == null)
1340: return;
1341:
1342: // form the SQL delete statement
1343: String statement = "delete from " + m_resourcePropertyTableName
1344: + " where ( " + idField + " = ? )";
1345:
1346: Object fields[] = new Object[1];
1347: fields[0] = key == null ? caseId(r.getId()) : key;
1348:
1349: // process the delete statement
1350: m_sql.dbWrite(statement, fields);
1351: }
1352:
1353: /**
1354: * Form a string of n question marks with commas, for sql value statements, one for each item in the values array, or an empty string if null. If the fields are "(...)" values, use these instead of ?.
1355: *
1356: * @param values
1357: * The values to be inserted into the sql statement.
1358: * @return A sql statement fragment for the values part of an insert, one for each value in the array.
1359: */
1360: protected String valuesParams(String[] fields, String dbidField) {
1361: StringBuffer buf = new StringBuffer();
1362: for (int i = 0; i < fields.length - 1; i++) {
1363: if (fields[i].startsWith("(")) {
1364: buf.append(fields[i]);
1365: } else {
1366: buf.append("?");
1367: }
1368: buf.append(",");
1369: }
1370:
1371: // for the last field
1372: if (fields[fields.length - 1].startsWith("(")) {
1373: buf.append(fields[fields.length - 1]);
1374: } else {
1375: buf.append("?");
1376: }
1377:
1378: if (dbidField != null) {
1379: if ("oracle".equals(m_sql.getVendor())) {
1380: // insert the sequence next value based on the table name value for a dbid field
1381: buf.append(",");
1382: buf.append(m_resourceTableName);
1383: buf.append("_SEQ.NEXTVAL");
1384: } else if ("mysql".equals(m_sql.getVendor())) {
1385: // for mysql, the field will auto increment as part of the schema...
1386: } else
1387: // if ("hsqldb".equals(m_sql.getVendor()))
1388: {
1389: // insert the sequence next value based on the table name value for a dbid field
1390: buf.append(", NEXT VALUE FOR ");
1391: buf.append(m_resourceTableName);
1392: buf.append("_SEQ");
1393: }
1394: }
1395:
1396: return buf.toString();
1397: }
1398:
1399: /**
1400: * Form a string of n name=?, for sql update set statements, one for each item in the values array, or an empty string if null.
1401: *
1402: * @param values
1403: * The values to be inserted into the sql statement.
1404: * @return A sql statement fragment for the values part of an insert, one for each value in the array.
1405: */
1406: protected String updateSet(String[] fields) {
1407: StringBuffer buf = new StringBuffer();
1408:
1409: // we assume the first field is the primary key, and we don't want to include that in the update, so start at 1
1410: for (int i = 1; i < fields.length; i++) {
1411: buf.append(fields[i]);
1412:
1413: // if the "field" contains an equals, assume it's a complete field=value statement, else add an =?
1414: if (fields[i].indexOf("=") == -1) {
1415: buf.append(" = ?");
1416: }
1417: buf.append(",");
1418: }
1419:
1420: // take off the last comma
1421: buf.setLength(buf.length() - 1);
1422:
1423: return buf.toString();
1424: }
1425:
1426: /**
1427: * For update, we don't want to include the first, primary key, field, so strip it off
1428: *
1429: * @param fields
1430: * The full set of fields
1431: * @return The fields with the first removed
1432: */
1433: protected Object[] updateFields(Object[] fields) {
1434: if (fields == null)
1435: return null;
1436:
1437: Object updateFields[] = new Object[fields.length - 1];
1438:
1439: System.arraycopy(fields, 1, updateFields, 0,
1440: updateFields.length);
1441:
1442: return updateFields;
1443: }
1444:
1445: /**
1446: * Form a string of field, field, field - one for each item in the fields array.
1447: *
1448: * @param fields
1449: * The field names.
1450: * @return A string of field, field, field - one for each item in the fields array.
1451: */
1452: protected String fieldList(String[] fields, String dbidField) {
1453: StringBuffer buf = new StringBuffer();
1454:
1455: for (int i = 0; i < fields.length - 1; i++) {
1456: buf.append(qualifyField(fields[i], m_resourceTableName)
1457: + ",");
1458: }
1459:
1460: buf.append(qualifyField(fields[fields.length - 1],
1461: m_resourceTableName));
1462:
1463: if (dbidField != null) {
1464: if (!"mysql".equals(m_sql.getVendor())) {
1465: // MySQL doesn't need this field, but Oracle and HSQLDB do
1466: buf.append(","
1467: + qualifyField(dbidField, m_resourceTableName));
1468: }
1469: }
1470:
1471: return buf.toString();
1472: }
1473:
1474: /**
1475: * Qualify the fiel with the table name, if it's a field.
1476: *
1477: * @param field
1478: * The field.
1479: * @param table
1480: * The table name.
1481: * @return The field name qualified with the table name.
1482: */
1483: protected String qualifyField(String field, String table) {
1484: // if it's not a field but a sub-select, don't qualify
1485: if (field.startsWith("(")) {
1486: return field;
1487: }
1488:
1489: else {
1490: return table + "." + field;
1491: }
1492: }
1493:
1494: /**
1495: * Fix the case of resource ids to support case insensitive ids if enabled
1496: *
1497: * @param The
1498: * id to fix.
1499: * @return The id, case modified as needed.
1500: */
1501: protected String caseId(String id) {
1502: if (m_caseInsensitive) {
1503: return id.toLowerCase();
1504: }
1505:
1506: return id;
1507: }
1508:
1509: /**
1510: * Enable / disable case insensitive ids.
1511: *
1512: * @param setting
1513: * true to set case insensitivity, false to set case sensitivity.
1514: */
1515: protected void setCaseInsensitivity(boolean setting) {
1516: m_caseInsensitive = setting;
1517: }
1518:
1519: /**
1520: * Return a record ID to use internally in the database. This is needed for databases (MySQL) that have limits on key lengths. The hash code ensures that the record ID will be unique, even if the DB only considers a prefix of a very long record ID.
1521: *
1522: * @param recordId
1523: * @return The record ID to use internally in the database
1524: */
1525: private String internalRecordId(String recordId) {
1526: if ("mysql".equals(m_sql.getVendor())) {
1527: if (recordId == null)
1528: recordId = "null";
1529: return recordId.hashCode() + " - " + recordId;
1530: } else
1531: // oracle, hsqldb
1532: {
1533: return recordId;
1534: }
1535: }
1536: }
|