001: package simpleorm.core;
002:
003: import java.sql.Connection;
004: import java.sql.PreparedStatement;
005:
006: import simpleorm.core.SRecordInstance.BrokenOptimisticLockException;
007:
008: /**
009: * Overflow from SRecordInstance, all the flushing logic.
010: * Instance per SRecordMeta.
011: */
012: class SRecordUpdater {
013:
014: static void flush(SRecordInstance instance) {
015: SRecordMeta meta = instance.getMeta();
016: if (SLog.slog.enableUpdates())
017: SLog.slog.updates("Flushing '"
018: + instance
019: + "'"
020: + (instance.newRow ? "INSERT"
021: : instance.deleted ? "DELETE" : "UPDATE"));
022:
023: if (!instance.isDirty())
024: return;
025: // No error to attempt to flush clean record.
026: if (instance.updateListIndex == -1)
027: throw new SException.Error(
028: "Record isDirty but not in updateList (detached?) "
029: + instance);
030:
031: instance.validateRecord();
032:
033: SConnection scon = SConnection.getBegunConnection();
034:
035: if (instance.sConnection != scon)
036: throw new SException.Error("Inconsistent Connections "
037: + instance + instance.sConnection + scon);
038:
039: /// Determine fields to be updated.
040: SArrayList fieldList = new SArrayList(meta.sFieldMetas.size());
041: if (!instance.deleted) {
042: for (int ux = 0; ux < meta.sFieldMetas.size(); ux++) {
043: SFieldMeta sfm = (SFieldMeta) meta.sFieldMetas.get(ux);
044: boolean upd = (instance.newRow && sfm.isPrimaryKey);
045: upd = upd
046: || (instance.bitSets[sfm.fieldIndex] & SCon.INS_DIRTY) != 0;
047: upd = upd && (instance.newRow || !sfm.isPrimaryKey);
048: // Primarly keys are marked dirty if retrieved via findOrCreate.
049: if (upd && !(sfm instanceof SFieldReference))
050: fieldList.add(sfm);
051: }
052: }
053: if (!(instance.newRow && instance.deleted)
054: && (instance.deleted || fieldList.size() > 0)) {
055:
056: /// Determine the SQL Query
057: String qry = null;
058:
059: // Allocate max size, Object[] more efficient than SArrayList
060: Object[] keyMetaValues = new Object[meta.sFieldMetas.size()];
061:
062: SArrayList keyMetas = instance.keyFieldMetas(
063: !instance.newRow
064: && instance.optimisticFieldValues != null, // true --> Optimisitic
065: keyMetaValues);
066:
067: if (instance.newRow)
068: qry = scon.sDriver.insertSQL(fieldList, meta);
069: else if (instance.deleted)
070: qry = scon.sDriver.deleteSQL(meta, keyMetas, instance,
071: keyMetaValues);
072: else
073: qry = scon.sDriver.updateSQL(fieldList, meta, keyMetas,
074: instance, keyMetaValues);
075: // ## could cache the creation of these queries. But pretty
076: // fast anyway. Batching them is much more important.
077:
078: /// Prepare the statement
079: Connection con = scon.jdbcConnection;
080: PreparedStatement ps = null;
081: try {
082: ps = con.prepareStatement(qry); // Let the JDBC driver cache these.
083: } catch (Exception psex) {
084: throw new SException.JDBC("Preparing " + instance + "'"
085: + qry + "'", psex);
086: }
087:
088: /// setObject(,)s the new body values for INSERT or UPDATE statement
089: int jx = 0; // JDBC setString
090: SArrayList parameters = new SArrayList(20); // Just used for tracing
091: if (!instance.deleted) {
092: for (int vx = 0; vx < fieldList.size(); vx++) {
093: int pvx = ((SFieldMeta) fieldList.get(vx)).fieldIndex;
094: SFieldMeta fieldMeta = (SFieldMeta) fieldList
095: .get(vx);
096: try {
097: jx++;
098: parameters.add(instance.fieldValues[pvx]);
099:
100: // ### Maybe should dispach setObject to SField* drivers.
101: if (instance.fieldValues[pvx] == null)
102: ps.setNull(jx, java.sql.Types.VARCHAR);
103: // The VARCHAR is needed for Oracle, otherwise it
104: // complains that it does not know what type the null is
105: // (ie. with setObject(,null)). Odd. But varchar is safe
106: // for other types as everything can be coerced to a
107: // string. And as it is only null, nothing has to be
108: // coerced.
109: else
110: //ps.setObject(jx,fieldValues[pvx]);
111: // writeFieldValue converts True to "Y" etc.
112: fieldMeta.writeFieldValue(ps, jx,
113: instance.fieldValues[pvx]);
114: } catch (Exception se) {
115: throw new SException.JDBC("Setting Values "
116: + instance + "'" + qry + "' Field "
117: + (vx + 1), se);
118: }
119: }
120: }
121: /// setObject(,)s the key for UPDATE or DELETE statement. May include
122: /// optimisitic fields.
123: if (!instance.newRow) {
124: for (int kx = 0; kx < keyMetas.size(); kx++) {
125: SFieldMeta fieldMeta = (SFieldMeta) keyMetas
126: .get(kx);
127: int pkx = fieldMeta.fieldIndex;
128: Object value;
129: if (instance.optimisticFieldValues == null)
130: value = instance.fieldValues[pkx];
131: else {
132: try {
133: value = fieldMeta
134: .writeFieldValue(instance.optimisticFieldValues[pkx]);
135: } catch (Exception se) {
136: throw new SException.JDBC(
137: "Converting optimistic field value '"
138: + instance + " '" + qry
139: + "' " + jx
140: + keyMetas.get(kx) + " = "
141: + instance.fieldValues[pkx],
142: se);
143: }
144: }
145:
146: if (value != null) { // else generates IS NULL
147: jx++;
148: if (SLog.slog.enableFields())
149: SLog.slog.fields("Key set " + instance + jx
150: + keyMetas.get(kx) + " = " + value);
151: try {
152: parameters.add(value);
153: ps.setObject(jx, value);
154: } catch (Exception se) {
155: throw new SException.JDBC("Setting "
156: + instance + " '" + qry + "' " + jx
157: + keyMetas.get(kx) + " = " + value,
158: se);
159: }
160: }
161: }
162: }
163: /*
164: if (!newRow) {
165: for (int kx=0; kx<keyMetas.size(); kx++) {
166: int pkx = ((SFieldMeta)keyMetas.get(kx)).fieldIndex;
167: Object value
168: = optimisticFieldValues == null ? fieldValues[pkx]
169: : optimisticFieldValues[pkx];
170: if (value != null) { // else generates IS NULL
171: jx++;
172: if (SLog.slog.enableFields())
173: SLog.slog.fields("Key set " + instance + jx + keyMetas.get(kx) + " = " + value);
174: try {
175: parameters.add( value );
176: ps.setObject(jx, value);
177: } catch (Exception se) {
178: throw new SException.JDBC("Setting " + instance + " '" + qry
179: + "' " + jx + keyMetas.get(kx) + " = " + value , se);
180: }
181: }
182: }
183: }
184: */
185:
186: // Logging
187: if (SLog.slog.enableQueries())
188: SLog.slog.queries("FlushSQL "
189: + SQuery.substitute(qry, parameters));
190:
191: /// Execute the Query
192: int result = 0;
193: try {
194: result = ps.executeUpdate();
195: } catch (Exception rsex) {
196: throw new SException.JDBC("Executing " + qry + " for "
197: + instance, rsex);
198: }
199: if (result != 1) {
200: if (instance.optimisticFieldValues != null
201: && result == 0)
202: throw new BrokenOptimisticLockException(instance);
203: else
204: throw new SException.InternalError("Rows Updated "
205: + result + " != 1 " + instance);
206: }
207: try {
208: ps.close();
209: } catch (Exception cl1) {
210: throw new SException.JDBC("Closing " + instance, cl1);
211: }
212:
213: }
214:
215: /// Clear dirty and Remove this from updateList
216: for (int fdx = 0; fdx < fieldList.size(); fdx++) {
217: SFieldMeta field = (SFieldMeta) fieldList.get(fdx);
218: int fdfx = field.fieldIndex;
219: instance.bitSets[fdfx] &= ~SCon.INS_DIRTY; // leave valid bit set.
220:
221: // Also clear dirty-flag for SFieldReference
222: if (field.sFieldReference != null) {
223: instance.bitSets[field.sFieldReference.fieldIndex] &= ~SCon.INS_DIRTY;
224: }
225: }
226: scon.uncommittedFlushes = true;
227: scon.updateList.set(instance.updateListIndex, null);
228: instance.updateListIndex = -1;
229: instance.dirty = false;
230: instance.newRow = false; // Any subsequent flush should be an UPDATE.
231: // If was deleted leave as deleted -- attempts to access will fail.
232: // if newRow && deleted OK to leave in updateListIndex.
233: if (instance.optimisticFieldValues != null)
234: instance.setOptimistic(true); // In case the record is flushed a second time.
235: } // flush
236:
237: }
|