History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/rowset/internal/CachedRowSetWriter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/rowset/internal/CachedRowSetWriter.java+1440−0
@@ -0,0 +1,1440 @@
1+/*
2+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package com.sun.rowset.internal;
27+
28+import java.sql.*;
29+import javax.sql.*;
30+import java.util.*;
31+import java.io.*;
32+
33+import com.sun.rowset.*;
34+import java.text.MessageFormat;
35+import javax.sql.rowset.*;
36+import javax.sql.rowset.serial.SQLInputImpl;
37+import javax.sql.rowset.serial.SerialArray;
38+import javax.sql.rowset.serial.SerialBlob;
39+import javax.sql.rowset.serial.SerialClob;
40+import javax.sql.rowset.serial.SerialStruct;
41+import javax.sql.rowset.spi.*;
42+
43+
44+/**
45+ * The facility called on internally by the <code>RIOptimisticProvider</code> implementation to
46+ * propagate changes back to the data source from which the rowset got its data.
47+ * <P>
48+ * A <code>CachedRowSetWriter</code> object, called a writer, has the public
49+ * method <code>writeData</code> for writing modified data to the underlying data source.
50+ * This method is invoked by the rowset internally and is never invoked directly by an application.
51+ * A writer also has public methods for setting and getting
52+ * the <code>CachedRowSetReader</code> object, called a reader, that is associated
53+ * with the writer. The remainder of the methods in this class are private and
54+ * are invoked internally, either directly or indirectly, by the method
55+ * <code>writeData</code>.
56+ * <P>
57+ * Typically the <code>SyncFactory</code> manages the <code>RowSetReader</code> and
58+ * the <code>RowSetWriter</code> implementations using <code>SyncProvider</code> objects.
59+ * Standard JDBC RowSet implementations provide an object instance of this
60+ * writer by invoking the <code>SyncProvider.getRowSetWriter()</code> method.
61+ *
62+ * @version 0.2
63+ * @author Jonathan Bruce
64+ * @see javax.sql.rowset.spi.SyncProvider
65+ * @see javax.sql.rowset.spi.SyncFactory
66+ * @see javax.sql.rowset.spi.SyncFactoryException
67+ */
68+public class CachedRowSetWriter implements TransactionalWriter, Serializable {
69+
70+/**
71+ * The <code>Connection</code> object that this writer will use to make a
72+ * connection to the data source to which it will write data.
73+ *
74+ */
75+ private transient Connection con;
76+
77+/**
78+ * The SQL <code>SELECT</code> command that this writer will call
79+ * internally. The method <code>initSQLStatements</code> builds this
80+ * command by supplying the words "SELECT" and "FROM," and using
81+ * metadata to get the table name and column names .
82+ *
83+ * @serial
84+ */
85+ private String selectCmd;
86+
87+/**
88+ * The SQL <code>UPDATE</code> command that this writer will call
89+ * internally to write data to the rowset's underlying data source.
90+ * The method <code>initSQLStatements</code> builds this <code>String</code>
91+ * object.
92+ *
93+ * @serial
94+ */
95+ private String updateCmd;
96+
97+/**
98+ * The SQL <code>WHERE</code> clause the writer will use for update
99+ * statements in the <code>PreparedStatement</code> object
100+ * it sends to the underlying data source.
101+ *
102+ * @serial
103+ */
104+ private String updateWhere;
105+
106+/**
107+ * The SQL <code>DELETE</code> command that this writer will call
108+ * internally to delete a row in the rowset's underlying data source.
109+ *
110+ * @serial
111+ */
112+ private String deleteCmd;
113+
114+/**
115+ * The SQL <code>WHERE</code> clause the writer will use for delete
116+ * statements in the <code>PreparedStatement</code> object
117+ * it sends to the underlying data source.
118+ *
119+ * @serial
120+ */
121+ private String deleteWhere;
122+
123+/**
124+ * The SQL <code>INSERT INTO</code> command that this writer will internally use
125+ * to insert data into the rowset's underlying data source. The method
126+ * <code>initSQLStatements</code> builds this command with a question
127+ * mark parameter placeholder for each column in the rowset.
128+ *
129+ * @serial
130+ */
131+ private String insertCmd;
132+
133+/**
134+ * An array containing the column numbers of the columns that are
135+ * needed to uniquely identify a row in the <code>CachedRowSet</code> object
136+ * for which this <code>CachedRowSetWriter</code> object is the writer.
137+ *
138+ * @serial
139+ */
140+ private int[] keyCols;
141+
142+/**
143+ * An array of the parameters that should be used to set the parameter
144+ * placeholders in a <code>PreparedStatement</code> object that this
145+ * writer will execute.
146+ *
147+ * @serial
148+ */
149+ private Object[] params;
150+
151+/**
152+ * The <code>CachedRowSetReader</code> object that has been
153+ * set as the reader for the <code>CachedRowSet</code> object
154+ * for which this <code>CachedRowSetWriter</code> object is the writer.
155+ *
156+ * @serial
157+ */
158+ private CachedRowSetReader reader;
159+
160+/**
161+ * The <code>ResultSetMetaData</code> object that contains information
162+ * about the columns in the <code>CachedRowSet</code> object
163+ * for which this <code>CachedRowSetWriter</code> object is the writer.
164+ *
165+ * @serial
166+ */
167+ private ResultSetMetaData callerMd;
168+
169+/**
170+ * The number of columns in the <code>CachedRowSet</code> object
171+ * for which this <code>CachedRowSetWriter</code> object is the writer.
172+ *
173+ * @serial
174+ */
175+ private int callerColumnCount;
176+
177+/**
178+ * This <code>CachedRowSet<code> will hold the conflicting values
179+ * retrieved from the db and hold it.
180+ */
181+ private CachedRowSetImpl crsResolve;
182+
183+/**
184+ * This <code>ArrayList<code> will hold the values of SyncResolver.*
185+ */
186+ private ArrayList status;
187+
188+/**
189+ * This will check whether the same field value has changed both
190+ * in database and CachedRowSet.
191+ */
192+ private int iChangedValsInDbAndCRS;
193+
194+/**
195+ * This will hold the number of cols for which the values have
196+ * changed only in database.
197+ */
198+ private int iChangedValsinDbOnly ;
199+
200+ private JdbcRowSetResourceBundle resBundle;
201+
202+ public CachedRowSetWriter() {
203+ try {
204+ resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
205+ } catch(IOException ioe) {
206+ throw new RuntimeException(ioe);
207+ }
208+ }
209+
210+/**
211+ * Propagates changes in the given <code>RowSet</code> object
212+ * back to its underlying data source and returns <code>true</code>
213+ * if successful. The writer will check to see if
214+ * the data in the pre-modified rowset (the original values) differ
215+ * from the data in the underlying data source. If data in the data
216+ * source has been modified by someone else, there is a conflict,
217+ * and in that case, the writer will not write to the data source.
218+ * In other words, the writer uses an optimistic concurrency algorithm:
219+ * It checks for conflicts before making changes rather than restricting
220+ * access for concurrent users.
221+ * <P>
222+ * This method is called by the rowset internally when
223+ * the application invokes the method <code>acceptChanges</code>.
224+ * The <code>writeData</code> method in turn calls private methods that
225+ * it defines internally.
226+ * The following is a general summary of what the method
227+ * <code>writeData</code> does, much of which is accomplished
228+ * through calls to its own internal methods.
229+ * <OL>
230+ * <LI>Creates a <code>CachedRowSet</code> object from the given
231+ * <code>RowSet</code> object
232+ * <LI>Makes a connection with the data source
233+ * <UL>
234+ * <LI>Disables autocommit mode if it is not already disabled
235+ * <LI>Sets the transaction isolation level to that of the rowset
236+ * </UL>
237+ * <LI>Checks to see if the reader has read new data since the writer
238+ * was last called and, if so, calls the method
239+ * <code>initSQLStatements</code> to initialize new SQL statements
240+ * <UL>
241+ * <LI>Builds new <code>SELECT</code>, <code>UPDATE</code>,
242+ * <code>INSERT</code>, and <code>DELETE</code> statements
243+ * <LI>Uses the <code>CachedRowSet</code> object's metadata to
244+ * determine the table name, column names, and the columns
245+ * that make up the primary key
246+ * </UL>
247+ * <LI>When there is no conflict, propagates changes made to the
248+ * <code>CachedRowSet</code> object back to its underlying data source
249+ * <UL>
250+ * <LI>Iterates through each row of the <code>CachedRowSet</code> object
251+ * to determine whether it has been updated, inserted, or deleted
252+ * <LI>If the corresponding row in the data source has not been changed
253+ * since the rowset last read its
254+ * values, the writer will use the appropriate command to update,
255+ * insert, or delete the row
256+ * <LI>If any data in the data source does not match the original values
257+ * for the <code>CachedRowSet</code> object, the writer will roll
258+ * back any changes it has made to the row in the data source.
259+ * </UL>
260+ * </OL>
261+ *
262+ * @return <code>true</code> if changes to the rowset were successfully
263+ * written to the rowset's underlying data source;
264+ * <code>false</code> otherwise
265+ */
266+ public boolean writeData(RowSetInternal caller) throws SQLException {
267+ boolean conflict = false;
268+ boolean showDel = false;
269+ PreparedStatement pstmtIns = null;
270+ iChangedValsInDbAndCRS = 0;
271+ iChangedValsinDbOnly = 0;
272+
273+ // We assume caller is a CachedRowSet
274+ CachedRowSetImpl crs = (CachedRowSetImpl)caller;
275+ // crsResolve = new CachedRowSetImpl();
276+ this.crsResolve = new CachedRowSetImpl();;
277+
278+ // The reader is registered with the writer at design time.
279+ // This is not required, in general. The reader has logic
280+ // to get a JDBC connection, so call it.
281+
282+ con = reader.connect(caller);
283+
284+
285+ if (con == null) {
286+ throw new SQLException(resBundle.handleGetObject("crswriter.connect").toString());
287+ }
288+
289+ /*
290+ // Fix 6200646.
291+ // Don't change the connection or transaction properties. This will fail in a
292+ // J2EE container.
293+ if (con.getAutoCommit() == true) {
294+ con.setAutoCommit(false);
295+ }
296+
297+ con.setTransactionIsolation(crs.getTransactionIsolation());
298+ */
299+
300+ initSQLStatements(crs);
301+ int iColCount;
302+
303+ RowSetMetaDataImpl rsmdWrite = (RowSetMetaDataImpl)crs.getMetaData();
304+ RowSetMetaDataImpl rsmdResolv = new RowSetMetaDataImpl();
305+
306+ iColCount = rsmdWrite.getColumnCount();
307+ int sz= crs.size()+1;
308+ status = new ArrayList(sz);
309+
310+ status.add(0,null);
311+ rsmdResolv.setColumnCount(iColCount);
312+
313+ for(int i =1; i <= iColCount; i++) {
314+ rsmdResolv.setColumnType(i, rsmdWrite.getColumnType(i));
315+ rsmdResolv.setColumnName(i, rsmdWrite.getColumnName(i));
316+ rsmdResolv.setNullable(i, ResultSetMetaData.columnNullableUnknown);
317+ }
318+ this.crsResolve.setMetaData(rsmdResolv);
319+
320+ // moved outside the insert inner loop
321+ //pstmtIns = con.prepareStatement(insertCmd);
322+
323+ if (callerColumnCount < 1) {
324+ // No data, so return success.
325+ if (reader.getCloseConnection() == true)
326+ con.close();
327+ return true;
328+ }
329+ // We need to see rows marked for deletion.
330+ showDel = crs.getShowDeleted();
331+ crs.setShowDeleted(true);
332+
333+ // Look at all the rows.
334+ crs.beforeFirst();
335+
336+ int rows =1;
337+ while (crs.next()) {
338+ if (crs.rowDeleted()) {
339+ // The row has been deleted.
340+ if (conflict = (deleteOriginalRow(crs, this.crsResolve)) == true) {
341+ status.add(rows, Integer.valueOf(SyncResolver.DELETE_ROW_CONFLICT));
342+ } else {
343+ // delete happened without any occurrence of conflicts
344+ // so update status accordingly
345+ status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
346+ }
347+
348+ } else if (crs.rowInserted()) {
349+ // The row has been inserted.
350+
351+ pstmtIns = con.prepareStatement(insertCmd);
352+ if ( (conflict = insertNewRow(crs, pstmtIns, this.crsResolve)) == true) {
353+ status.add(rows, Integer.valueOf(SyncResolver.INSERT_ROW_CONFLICT));
354+ } else {
355+ // insert happened without any occurrence of conflicts
356+ // so update status accordingly
357+ status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
358+ }
359+ } else if (crs.rowUpdated()) {
360+ // The row has been updated.
361+ if ( conflict = (updateOriginalRow(crs)) == true) {
362+ status.add(rows, Integer.valueOf(SyncResolver.UPDATE_ROW_CONFLICT));
363+ } else {
364+ // update happened without any occurrence of conflicts
365+ // so update status accordingly
366+ status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
367+ }
368+
369+ } else {
370+ /** The row is neither of inserted, updated or deleted.
371+ * So set nulls in the this.crsResolve for this row,
372+ * as nothing is to be done for such rows.
373+ * Also note that if such a row has been changed in database
374+ * and we have not changed(inserted, updated or deleted)
375+ * that is fine.
376+ **/
377+ int icolCount = crs.getMetaData().getColumnCount();
378+ status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
379+
380+ this.crsResolve.moveToInsertRow();
381+ for(int cols=0;cols<iColCount;cols++) {
382+ this.crsResolve.updateNull(cols+1);
383+ } //end for
384+
385+ this.crsResolve.insertRow();
386+ this.crsResolve.moveToCurrentRow();
387+
388+ } //end if
389+ rows++;
390+ } //end while
391+
392+ // close the insert statement
393+ if(pstmtIns!=null)
394+ pstmtIns.close();
395+ // reset
396+ crs.setShowDeleted(showDel);
397+
398+ boolean boolConf = false;
399+ for (int j=1;j<status.size();j++){
400+ // ignore status for index = 0 which is set to null
401+ if(! ((status.get(j)).equals(Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)))) {
402+ // there is at least one conflict which needs to be resolved
403+ boolConf = true;
404+ break;
405+ }
406+ }
407+
408+ crs.beforeFirst();
409+ this.crsResolve.beforeFirst();
410+
411+ if(boolConf) {
412+ SyncProviderException spe = new SyncProviderException(status.size() - 1+resBundle.handleGetObject("crswriter.conflictsno").toString());
413+ //SyncResolver syncRes = spe.getSyncResolver();
414+
415+ SyncResolverImpl syncResImpl = (SyncResolverImpl) spe.getSyncResolver();
416+
417+ syncResImpl.setCachedRowSet(crs);
418+ syncResImpl.setCachedRowSetResolver(this.crsResolve);
419+
420+ syncResImpl.setStatus(status);
421+ syncResImpl.setCachedRowSetWriter(this);
422+
423+ throw spe;
424+ } else {
425+ return true;
426+ }
427+ /*
428+ if (conflict == true) {
429+ con.rollback();
430+ return false;
431+ } else {
432+ con.commit();
433+ if (reader.getCloseConnection() == true) {
434+ con.close();
435+ }
436+ return true;
437+ }
438+ */
439+
440+ } //end writeData
441+
442+/**
443+ * Updates the given <code>CachedRowSet</code> object's underlying data
444+ * source so that updates to the rowset are reflected in the original
445+ * data source, and returns <code>false</code> if the update was successful.
446+ * A return value of <code>true</code> indicates that there is a conflict,
447+ * meaning that a value updated in the rowset has already been changed by
448+ * someone else in the underlying data source. A conflict can also exist
449+ * if, for example, more than one row in the data source would be affected
450+ * by the update or if no rows would be affected. In any case, if there is
451+ * a conflict, this method does not update the underlying data source.
452+ * <P>
453+ * This method is called internally by the method <code>writeData</code>
454+ * if a row in the <code>CachedRowSet</code> object for which this
455+ * <code>CachedRowSetWriter</code> object is the writer has been updated.
456+ *
457+ * @return <code>false</code> if the update to the underlying data source is
458+ * successful; <code>true</code> otherwise
459+ * @throws SQLException if a database access error occurs
460+ */
461+ private boolean updateOriginalRow(CachedRowSet crs)
462+ throws SQLException {
463+ PreparedStatement pstmt;
464+ int i = 0;
465+ int idx = 0;
466+
467+ // Select the row from the database.
468+ ResultSet origVals = crs.getOriginalRow();
469+ origVals.next();
470+
471+ try {
472+ updateWhere = buildWhereClause(updateWhere, origVals);
473+
474+
475+ /**
476+ * The following block of code is for checking a particular type of
477+ * query where in there is a where clause. Without this block, if a
478+ * SQL statement is built the "where" clause will appear twice hence
479+ * the DB errors out and a SQLException is thrown. This code also
480+ * considers that the where clause is in the right place as the
481+ * CachedRowSet object would already have been populated with this
482+ * query before coming to this point.
483+ **/
484+
485+
486+ String tempselectCmd = selectCmd.toLowerCase();
487+
488+ int idxWhere = tempselectCmd.indexOf("where");
489+
490+ if(idxWhere != -1)
491+ {
492+ String tempSelect = selectCmd.substring(0,idxWhere);
493+ selectCmd = tempSelect;
494+ }
495+
496+ pstmt = con.prepareStatement(selectCmd + updateWhere,
497+ ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
498+
499+ for (i = 0; i < keyCols.length; i++) {
500+ if (params[i] != null) {
501+ pstmt.setObject(++idx, params[i]);
502+ } else {
503+ continue;
504+ }
505+ }
506+
507+ try {
508+ pstmt.setMaxRows(crs.getMaxRows());
509+ pstmt.setMaxFieldSize(crs.getMaxFieldSize());
510+ pstmt.setEscapeProcessing(crs.getEscapeProcessing());
511+ pstmt.setQueryTimeout(crs.getQueryTimeout());
512+ } catch (Exception ex) {
513+ // Older driver don't support these operations.
514+ }
515+
516+ ResultSet rs = null;
517+ rs = pstmt.executeQuery();
518+ ResultSetMetaData rsmd = rs.getMetaData();
519+
520+ if (rs.next()) {
521+ if (rs.next()) {
522+ /** More than one row conflict.
523+ * If rs has only one row we are able to
524+ * uniquely identify the row where update
525+ * have to happen else if more than one
526+ * row implies we cannot uniquely identify the row
527+ * where we have to do updates.
528+ * crs.setKeyColumns needs to be set to
529+ * come out of this situation.
530+ */
531+
532+ return true;
533+ }
534+
535+ // don't close the rs
536+ // we require the record in rs to be used.
537+ // rs.close();
538+ // pstmt.close();
539+ rs.first();
540+
541+ // how many fields need to be updated
542+ int colsNotChanged = 0;
543+ Vector cols = new Vector();
544+ String updateExec = updateCmd;
545+ Object orig;
546+ Object curr;
547+ Object rsval;
548+ boolean boolNull = true;
549+ Object objVal = null;
550+
551+ // There's only one row and the cursor
552+ // needs to be on that row.
553+
554+ boolean first = true;
555+ boolean flag = true;
556+
557+ this.crsResolve.moveToInsertRow();
558+
559+ for (i = 1; i <= callerColumnCount; i++) {
560+ orig = origVals.getObject(i);
561+ curr = crs.getObject(i);
562+ rsval = rs.getObject(i);
563+ /*
564+ * the following block creates equivalent objects
565+ * that would have been created if this rs is populated
566+ * into a CachedRowSet so that comparison of the column values
567+ * from the ResultSet and CachedRowSet are possible
568+ */
569+ Map map = (crs.getTypeMap() == null)?con.getTypeMap():crs.getTypeMap();
570+ if (rsval instanceof Struct) {
571+
572+ Struct s = (Struct)rsval;
573+
574+ // look up the class in the map
575+ Class c = null;
576+ c = (Class)map.get(s.getSQLTypeName());
577+ if (c != null) {
578+ // create new instance of the class
579+ SQLData obj = null;
580+ try {
581+ obj = (SQLData)c.newInstance();
582+ } catch (java.lang.InstantiationException ex) {
583+ throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
584+ ex.getMessage()));
585+ } catch (java.lang.IllegalAccessException ex) {
586+ throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
587+ ex.getMessage()));
588+ }
589+ // get the attributes from the struct
590+ Object attribs[] = s.getAttributes(map);
591+ // create the SQLInput "stream"
592+ SQLInputImpl sqlInput = new SQLInputImpl(attribs, map);
593+ // read the values...
594+ obj.readSQL(sqlInput, s.getSQLTypeName());
595+ rsval = obj;
596+ }
597+ } else if (rsval instanceof SQLData) {
598+ rsval = new SerialStruct((SQLData)rsval, map);
599+ } else if (rsval instanceof Blob) {
600+ rsval = new SerialBlob((Blob)rsval);
601+ } else if (rsval instanceof Clob) {
602+ rsval = new SerialClob((Clob)rsval);
603+ } else if (rsval instanceof java.sql.Array) {
604+ rsval = new SerialArray((java.sql.Array)rsval, map);
605+ }
606+
607+ // reset boolNull if it had been set
608+ boolNull = true;
609+
610+ /** This addtional checking has been added when the current value
611+ * in the DB is null, but the DB had a different value when the
612+ * data was actaully fetched into the CachedRowSet.
613+ **/
614+
615+ if(rsval == null && orig != null) {
616+ // value in db has changed
617+ // don't proceed with synchronization
618+ // get the value in db and pass it to the resolver.
619+
620+ iChangedValsinDbOnly++;
621+ // Set the boolNull to false,
622+ // in order to set the actual value;
623+ boolNull = false;
624+ objVal = rsval;
625+ }
626+
627+ /** Adding the checking for rsval to be "not" null or else
628+ * it would through a NullPointerException when the values
629+ * are compared.
630+ **/
631+
632+ else if(rsval != null && (!rsval.equals(orig)))
633+ {
634+ // value in db has changed
635+ // don't proceed with synchronization
636+ // get the value in db and pass it to the resolver.
637+
638+ iChangedValsinDbOnly++;
639+ // Set the boolNull to false,
640+ // in order to set the actual value;
641+ boolNull = false;
642+ objVal = rsval;
643+ } else if ( (orig == null || curr == null) ) {
644+
645+ /** Adding the additonal condition of checking for "flag"
646+ * boolean variable, which would otherwise result in
647+ * building a invalid query, as the comma would not be
648+ * added to the query string.
649+ **/
650+
651+ if (first == false || flag == false) {
652+ updateExec += ", ";
653+ }
654+ updateExec += crs.getMetaData().getColumnName(i);
655+ cols.add(Integer.valueOf(i));
656+ updateExec += " = ? ";
657+ first = false;
658+
659+ /** Adding the extra condition for orig to be "not" null as the
660+ * condition for orig to be null is take prior to this, if this
661+ * is not added it will result in a NullPointerException when
662+ * the values are compared.
663+ **/
664+
665+ } else if (orig.equals(curr)) {
666+ colsNotChanged++;
667+ //nothing to update in this case since values are equal
668+
669+ /** Adding the extra condition for orig to be "not" null as the
670+ * condition for orig to be null is take prior to this, if this
671+ * is not added it will result in a NullPointerException when
672+ * the values are compared.
673+ **/
674+
675+ } else if(orig.equals(curr) == false) {
676+ // When values from db and values in CachedRowSet are not equal,
677+ // if db value is same as before updation for each col in
678+ // the row before fetching into CachedRowSet,
679+ // only then we go ahead with updation, else we
680+ // throw SyncProviderException.
681+
682+ // if value has changed in db after fetching from db
683+ // for some cols of the row and at the same time, some other cols
684+ // have changed in CachedRowSet, no synchronization happens
685+
686+ // Synchronization happens only when data when fetching is
687+ // same or at most has changed in cachedrowset
688+
689+ // check orig value with what is there in crs for a column
690+ // before updation in crs.
691+
692+ if(crs.columnUpdated(i)) {
693+ if(rsval.equals(orig)) {
694+ // At this point we are sure that
695+ // the value updated in crs was from
696+ // what is in db now and has not changed
697+ if (flag == false || first == false) {
698+ updateExec += ", ";
699+ }
700+ updateExec += crs.getMetaData().getColumnName(i);
701+ cols.add(Integer.valueOf(i));
702+ updateExec += " = ? ";
703+ flag = false;
704+ } else {
705+ // Here the value has changed in the db after
706+ // data was fetched
707+ // Plus store this row from CachedRowSet and keep it
708+ // in a new CachedRowSet
709+ boolNull= false;
710+ objVal = rsval;
711+ iChangedValsInDbAndCRS++;
712+ }
713+ }
714+ }
715+
716+ if(!boolNull) {
717+ this.crsResolve.updateObject(i,objVal);
718+ } else {
719+ this.crsResolve.updateNull(i);
720+ }
721+ } //end for
722+
723+ rs.close();
724+ pstmt.close();
725+
726+ this.crsResolve.insertRow();
727+ this.crsResolve.moveToCurrentRow();
728+
729+ /**
730+ * if nothing has changed return now - this can happen
731+ * if column is updated to the same value.
732+ * if colsNotChanged == callerColumnCount implies we are updating
733+ * the database with ALL COLUMNS HAVING SAME VALUES,
734+ * so skip going to database, else do as usual.
735+ **/
736+ if ( (first == false && cols.size() == 0) ||
737+ colsNotChanged == callerColumnCount ) {
738+ return false;
739+ }
740+
741+ if(iChangedValsInDbAndCRS != 0 || iChangedValsinDbOnly != 0) {
742+ return true;
743+ }
744+
745+
746+ updateExec += updateWhere;
747+
748+ pstmt = con.prepareStatement(updateExec);
749+
750+ // Comments needed here
751+ for (i = 0; i < cols.size(); i++) {
752+ Object obj = crs.getObject(((Integer)cols.get(i)).intValue());
753+ if (obj != null)
754+ pstmt.setObject(i + 1, obj);
755+ else
756+ pstmt.setNull(i + 1,crs.getMetaData().getColumnType(i + 1));
757+ }
758+ idx = i;
759+
760+ // Comments needed here
761+ for (i = 0; i < keyCols.length; i++) {
762+ if (params[i] != null) {
763+ pstmt.setObject(++idx, params[i]);
764+ } else {
765+ continue;
766+ }
767+ }
768+
769+ i = pstmt.executeUpdate();
770+
771+ /**
772+ * i should be equal to 1(row count), because we update
773+ * one row(returned as row count) at a time, if all goes well.
774+ * if 1 != 1, this implies we have not been able to
775+ * do updations properly i.e there is a conflict in database
776+ * versus what is in CachedRowSet for this particular row.
777+ **/
778+
779+ return false;
780+
781+ } else {
782+ /**
783+ * Cursor will be here, if the ResultSet may not return even a single row
784+ * i.e. we can't find the row where to update because it has been deleted
785+ * etc. from the db.
786+ * Present the whole row as null to user, to force null to be sync'ed
787+ * and hence nothing to be synced.
788+ *
789+ * NOTE:
790+ * ------
791+ * In the database if a column that is mapped to java.sql.Types.REAL stores
792+ * a Double value and is compared with value got from ResultSet.getFloat()
793+ * no row is retrieved and will throw a SyncProviderException. For details
794+ * see bug Id 5053830
795+ **/
796+ return true;
797+ }
798+ } catch (SQLException ex) {
799+ ex.printStackTrace();
800+ // if executeUpdate fails it will come here,
801+ // update crsResolve with null rows
802+ this.crsResolve.moveToInsertRow();
803+
804+ for(i = 1; i <= callerColumnCount; i++) {
805+ this.crsResolve.updateNull(i);
806+ }
807+
808+ this.crsResolve.insertRow();
809+ this.crsResolve.moveToCurrentRow();
810+
811+ return true;
812+ }
813+ }
814+
815+ /**
816+ * Inserts a row that has been inserted into the given
817+ * <code>CachedRowSet</code> object into the data source from which
818+ * the rowset is derived, returning <code>false</code> if the insertion
819+ * was successful.
820+ *
821+ * @param crs the <code>CachedRowSet</code> object that has had a row inserted
822+ * and to whose underlying data source the row will be inserted
823+ * @param pstmt the <code>PreparedStatement</code> object that will be used
824+ * to execute the insertion
825+ * @return <code>false</code> to indicate that the insertion was successful;
826+ * <code>true</code> otherwise
827+ * @throws SQLException if a database access error occurs
828+ */
829+ private boolean insertNewRow(CachedRowSet crs,
830+ PreparedStatement pstmt, CachedRowSetImpl crsRes) throws SQLException {
831+ int i = 0;
832+ int icolCount = crs.getMetaData().getColumnCount();
833+
834+ boolean returnVal = false;
835+ PreparedStatement pstmtSel = con.prepareStatement(selectCmd,
836+ ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
837+ ResultSet rs, rs2 = null;
838+ DatabaseMetaData dbmd = con.getMetaData();
839+ rs = pstmtSel.executeQuery();
840+ String table = crs.getTableName();
841+ rs2 = dbmd.getPrimaryKeys(null, null, table);
842+ String [] primaryKeys = new String[icolCount];
843+ int k = 0;
844+ while(rs2.next()) {
845+ String pkcolname = rs2.getString("COLUMN_NAME");
846+ primaryKeys[k] = pkcolname;
847+ k++;
848+ }
849+
850+ if(rs.next()) {
851+ for(int j=0;j<primaryKeys.length;j++) {
852+ if(primaryKeys[j] != null) {
853+ if(crs.getObject(primaryKeys[j]) == null){
854+ break;
855+ }
856+ String crsPK = (crs.getObject(primaryKeys[j])).toString();
857+ String rsPK = (rs.getObject(primaryKeys[j])).toString();
858+ if(crsPK.equals(rsPK)) {
859+ returnVal = true;
860+ this.crsResolve.moveToInsertRow();
861+ for(i = 1; i <= icolCount; i++) {
862+ String colname = (rs.getMetaData()).getColumnName(i);
863+ if(colname.equals(primaryKeys[j]))
864+ this.crsResolve.updateObject(i,rsPK);
865+ else
866+ this.crsResolve.updateNull(i);
867+ }
868+ this.crsResolve.insertRow();
869+ this.crsResolve.moveToCurrentRow();
870+ }
871+ }
872+ }
873+ }
874+ if(returnVal)
875+ return returnVal;
876+
877+ try {
878+ for (i = 1; i <= icolCount; i++) {
879+ Object obj = crs.getObject(i);
880+ if (obj != null) {
881+ pstmt.setObject(i, obj);
882+ } else {
883+ pstmt.setNull(i,crs.getMetaData().getColumnType(i));
884+ }
885+ }
886+
887+ i = pstmt.executeUpdate();
888+ return false;
889+
890+ } catch (SQLException ex) {
891+ /**
892+ * Cursor will come here if executeUpdate fails.
893+ * There can be many reasons why the insertion failed,
894+ * one can be violation of primary key.
895+ * Hence we cannot exactly identify why the insertion failed
896+ * Present the current row as a null row to the user.
897+ **/
898+ this.crsResolve.moveToInsertRow();
899+
900+ for(i = 1; i <= icolCount; i++) {
901+ this.crsResolve.updateNull(i);
902+ }
903+
904+ this.crsResolve.insertRow();
905+ this.crsResolve.moveToCurrentRow();
906+
907+ return true;
908+ }
909+ }
910+
911+/**
912+ * Deletes the row in the underlying data source that corresponds to
913+ * a row that has been deleted in the given <code> CachedRowSet</code> object
914+ * and returns <code>false</code> if the deletion was successful.
915+ * <P>
916+ * This method is called internally by this writer's <code>writeData</code>
917+ * method when a row in the rowset has been deleted. The values in the
918+ * deleted row are the same as those that are stored in the original row
919+ * of the given <code>CachedRowSet</code> object. If the values in the
920+ * original row differ from the row in the underlying data source, the row
921+ * in the data source is not deleted, and <code>deleteOriginalRow</code>
922+ * returns <code>true</code> to indicate that there was a conflict.
923+ *
924+ *
925+ * @return <code>false</code> if the deletion was successful, which means that
926+ * there was no conflict; <code>true</code> otherwise
927+ * @throws SQLException if there was a database access error
928+ */
929+ private boolean deleteOriginalRow(CachedRowSet crs, CachedRowSetImpl crsRes) throws SQLException {
930+ PreparedStatement pstmt;
931+ int i;
932+ int idx = 0;
933+ String strSelect;
934+ // Select the row from the database.
935+ ResultSet origVals = crs.getOriginalRow();
936+ origVals.next();
937+
938+ deleteWhere = buildWhereClause(deleteWhere, origVals);
939+ pstmt = con.prepareStatement(selectCmd + deleteWhere,
940+ ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
941+
942+ for (i = 0; i < keyCols.length; i++) {
943+ if (params[i] != null) {
944+ pstmt.setObject(++idx, params[i]);
945+ } else {
946+ continue;
947+ }
948+ }
949+
950+ try {
951+ pstmt.setMaxRows(crs.getMaxRows());
952+ pstmt.setMaxFieldSize(crs.getMaxFieldSize());
953+ pstmt.setEscapeProcessing(crs.getEscapeProcessing());
954+ pstmt.setQueryTimeout(crs.getQueryTimeout());
955+ } catch (Exception ex) {
956+ /*
957+ * Older driver don't support these operations...
958+ */
959+ ;
960+ }
961+
962+ ResultSet rs = pstmt.executeQuery();
963+
964+ if (rs.next() == true) {
965+ if (rs.next()) {
966+ // more than one row
967+ return true;
968+ }
969+ rs.first();
970+
971+ // Now check all the values in rs to be same in
972+ // db also before actually going ahead with deleting
973+ boolean boolChanged = false;
974+
975+ crsRes.moveToInsertRow();
976+
977+ for (i = 1; i <= crs.getMetaData().getColumnCount(); i++) {
978+
979+ Object original = origVals.getObject(i);
980+ Object changed = rs.getObject(i);
981+
982+ if(original != null && changed != null ) {
983+ if(! (original.toString()).equals(changed.toString()) ) {
984+ boolChanged = true;
985+ crsRes.updateObject(i,origVals.getObject(i));
986+ }
987+ } else {
988+ crsRes.updateNull(i);
989+ }
990+ }
991+
992+ crsRes.insertRow();
993+ crsRes.moveToCurrentRow();
994+
995+ if(boolChanged) {
996+ // do not delete as values in db have changed
997+ // deletion will not happen for this row from db
998+ // exit now returning true. i.e. conflict
999+ return true;
1000+ } else {
1001+ // delete the row.
1002+ // Go ahead with deleting,
1003+ // don't do anything here
1004+ }
1005+
1006+ String cmd = deleteCmd + deleteWhere;
1007+ pstmt = con.prepareStatement(cmd);
1008+
1009+ idx = 0;
1010+ for (i = 0; i < keyCols.length; i++) {
1011+ if (params[i] != null) {
1012+ pstmt.setObject(++idx, params[i]);
1013+ } else {
1014+ continue;
1015+ }
1016+ }
1017+
1018+ if (pstmt.executeUpdate() != 1) {
1019+ return true;
1020+ }
1021+ pstmt.close();
1022+ } else {
1023+ // didn't find the row
1024+ return true;
1025+ }
1026+
1027+ // no conflict
1028+ return false;
1029+ }
1030+
1031+ /**
1032+ * Sets the reader for this writer to the given reader.
1033+ *
1034+ * @throws SQLException if a database access error occurs
1035+ */
1036+ public void setReader(CachedRowSetReader reader) throws SQLException {
1037+ this.reader = reader;
1038+ }
1039+
1040+ /**
1041+ * Gets the reader for this writer.
1042+ *
1043+ * @throws SQLException if a database access error occurs
1044+ */
1045+ public CachedRowSetReader getReader() throws SQLException {
1046+ return reader;
1047+ }
1048+
1049+ /**
1050+ * Composes a <code>SELECT</code>, <code>UPDATE</code>, <code>INSERT</code>,
1051+ * and <code>DELETE</code> statement that can be used by this writer to
1052+ * write data to the data source backing the given <code>CachedRowSet</code>
1053+ * object.
1054+ *
1055+ * @ param caller a <code>CachedRowSet</code> object for which this
1056+ * <code>CachedRowSetWriter</code> object is the writer
1057+ * @throws SQLException if a database access error occurs
1058+ */
1059+ private void initSQLStatements(CachedRowSet caller) throws SQLException {
1060+
1061+ int i;
1062+
1063+ callerMd = caller.getMetaData();
1064+ callerColumnCount = callerMd.getColumnCount();
1065+ if (callerColumnCount < 1)
1066+ // No data, so return.
1067+ return;
1068+
1069+ /*
1070+ * If the RowSet has a Table name we should use it.
1071+ * This is really a hack to get round the fact that
1072+ * a lot of the jdbc drivers can't provide the tab.
1073+ */
1074+ String table = caller.getTableName();
1075+ if (table == null) {
1076+ /*
1077+ * attempt to build a table name using the info
1078+ * that the driver gave us for the first column
1079+ * in the source result set.
1080+ */
1081+ table = callerMd.getTableName(1);
1082+ if (table == null || table.length() == 0) {
1083+ throw new SQLException(resBundle.handleGetObject("crswriter.tname").toString());
1084+ }
1085+ }
1086+ String catalog = callerMd.getCatalogName(1);
1087+ String schema = callerMd.getSchemaName(1);
1088+ DatabaseMetaData dbmd = con.getMetaData();
1089+
1090+ /*
1091+ * Compose a SELECT statement. There are three parts.
1092+ */
1093+
1094+ // Project List
1095+ selectCmd = "SELECT ";
1096+ for (i=1; i <= callerColumnCount; i++) {
1097+ selectCmd += callerMd.getColumnName(i);
1098+ if ( i < callerMd.getColumnCount() )
1099+ selectCmd += ", ";
1100+ else
1101+ selectCmd += " ";
1102+ }
1103+
1104+ // FROM clause.
1105+ selectCmd += "FROM " + buildTableName(dbmd, catalog, schema, table);
1106+
1107+ /*
1108+ * Compose an UPDATE statement.
1109+ */
1110+ updateCmd = "UPDATE " + buildTableName(dbmd, catalog, schema, table);
1111+
1112+
1113+ /**
1114+ * The following block of code is for checking a particular type of
1115+ * query where in there is a where clause. Without this block, if a
1116+ * SQL statement is built the "where" clause will appear twice hence
1117+ * the DB errors out and a SQLException is thrown. This code also
1118+ * considers that the where clause is in the right place as the
1119+ * CachedRowSet object would already have been populated with this
1120+ * query before coming to this point.
1121+ **/
1122+
1123+ String tempupdCmd = updateCmd.toLowerCase();
1124+
1125+ int idxupWhere = tempupdCmd.indexOf("where");
1126+
1127+ if(idxupWhere != -1)
1128+ {
1129+ updateCmd = updateCmd.substring(0,idxupWhere);
1130+ }
1131+ updateCmd += "SET ";
1132+
1133+ /*
1134+ * Compose an INSERT statement.
1135+ */
1136+ insertCmd = "INSERT INTO " + buildTableName(dbmd, catalog, schema, table);
1137+ // Column list
1138+ insertCmd += "(";
1139+ for (i=1; i <= callerColumnCount; i++) {
1140+ insertCmd += callerMd.getColumnName(i);
1141+ if ( i < callerMd.getColumnCount() )
1142+ insertCmd += ", ";
1143+ else
1144+ insertCmd += ") VALUES (";
1145+ }
1146+ for (i=1; i <= callerColumnCount; i++) {
1147+ insertCmd += "?";
1148+ if (i < callerColumnCount)
1149+ insertCmd += ", ";
1150+ else
1151+ insertCmd += ")";
1152+ }
1153+
1154+ /*
1155+ * Compose a DELETE statement.
1156+ */
1157+ deleteCmd = "DELETE FROM " + buildTableName(dbmd, catalog, schema, table);
1158+
1159+ /*
1160+ * set the key desriptors that will be
1161+ * needed to construct where clauses.
1162+ */
1163+ buildKeyDesc(caller);
1164+ }
1165+
1166+ /**
1167+ * Returns a fully qualified table name built from the given catalog and
1168+ * table names. The given metadata object is used to get the proper order
1169+ * and separator.
1170+ *
1171+ * @param dbmd a <code>DatabaseMetaData</code> object that contains metadata
1172+ * about this writer's <code>CachedRowSet</code> object
1173+ * @param catalog a <code>String</code> object with the rowset's catalog
1174+ * name
1175+ * @param table a <code>String</code> object with the name of the table from
1176+ * which this writer's rowset was derived
1177+ * @return a <code>String</code> object with the fully qualified name of the
1178+ * table from which this writer's rowset was derived
1179+ * @throws SQLException if a database access error occurs
1180+ */
1181+ private String buildTableName(DatabaseMetaData dbmd,
1182+ String catalog, String schema, String table) throws SQLException {
1183+
1184+ // trim all the leading and trailing whitespaces,
1185+ // white spaces can never be catalog, schema or a table name.
1186+
1187+ String cmd = "";
1188+
1189+ catalog = catalog.trim();
1190+ schema = schema.trim();
1191+ table = table.trim();
1192+
1193+ if (dbmd.isCatalogAtStart() == true) {
1194+ if (catalog != null && catalog.length() > 0) {
1195+ cmd += catalog + dbmd.getCatalogSeparator();
1196+ }
1197+ if (schema != null && schema.length() > 0) {
1198+ cmd += schema + ".";
1199+ }
1200+ cmd += table;
1201+ } else {
1202+ if (schema != null && schema.length() > 0) {
1203+ cmd += schema + ".";
1204+ }
1205+ cmd += table;
1206+ if (catalog != null && catalog.length() > 0) {
1207+ cmd += dbmd.getCatalogSeparator() + catalog;
1208+ }
1209+ }
1210+ cmd += " ";
1211+ return cmd;
1212+ }
1213+
1214+ /**
1215+ * Assigns to the given <code>CachedRowSet</code> object's
1216+ * <code>params</code>
1217+ * field an array whose length equals the number of columns needed
1218+ * to uniquely identify a row in the rowset. The array is given
1219+ * values by the method <code>buildWhereClause</code>.
1220+ * <P>
1221+ * If the <code>CachedRowSet</code> object's <code>keyCols</code>
1222+ * field has length <code>0</code> or is <code>null</code>, the array
1223+ * is set with the column number of every column in the rowset.
1224+ * Otherwise, the array in the field <code>keyCols</code> is set with only
1225+ * the column numbers of the columns that are required to form a unique
1226+ * identifier for a row.
1227+ *
1228+ * @param crs the <code>CachedRowSet</code> object for which this
1229+ * <code>CachedRowSetWriter</code> object is the writer
1230+ *
1231+ * @throws SQLException if a database access error occurs
1232+ */
1233+ private void buildKeyDesc(CachedRowSet crs) throws SQLException {
1234+
1235+ keyCols = crs.getKeyColumns();
1236+ ResultSetMetaData resultsetmd = crs.getMetaData();
1237+ if (keyCols == null || keyCols.length == 0) {
1238+ ArrayList<Integer> listKeys = new ArrayList<Integer>();
1239+
1240+ for (int i = 0; i < callerColumnCount; i++ ) {
1241+ if(resultsetmd.getColumnType(i+1) != java.sql.Types.CLOB &&
1242+ resultsetmd.getColumnType(i+1) != java.sql.Types.STRUCT &&
1243+ resultsetmd.getColumnType(i+1) != java.sql.Types.SQLXML &&
1244+ resultsetmd.getColumnType(i+1) != java.sql.Types.BLOB &&
1245+ resultsetmd.getColumnType(i+1) != java.sql.Types.ARRAY &&
1246+ resultsetmd.getColumnType(i+1) != java.sql.Types.OTHER )
1247+ listKeys.add(i+1);
1248+ }
1249+ keyCols = new int[listKeys.size()];
1250+ for (int i = 0; i < listKeys.size(); i++ )
1251+ keyCols[i] = listKeys.get(i);
1252+ }
1253+ params = new Object[keyCols.length];
1254+ }
1255+
1256+ /**
1257+ * Constructs an SQL <code>WHERE</code> clause using the given
1258+ * string as a starting point. The resulting clause will contain
1259+ * a column name and " = ?" for each key column, that is, each column
1260+ * that is needed to form a unique identifier for a row in the rowset.
1261+ * This <code>WHERE</code> clause can be added to
1262+ * a <code>PreparedStatement</code> object that updates, inserts, or
1263+ * deletes a row.
1264+ * <P>
1265+ * This method uses the given result set to access values in the
1266+ * <code>CachedRowSet</code> object that called this writer. These
1267+ * values are used to build the array of parameters that will serve as
1268+ * replacements for the "?" parameter placeholders in the
1269+ * <code>PreparedStatement</code> object that is sent to the
1270+ * <code>CachedRowSet</code> object's underlying data source.
1271+ *
1272+ * @param whereClause a <code>String</code> object that is an empty
1273+ * string ("")
1274+ * @param rs a <code>ResultSet</code> object that can be used
1275+ * to access the <code>CachedRowSet</code> object's data
1276+ * @return a <code>WHERE</code> clause of the form "<code>WHERE</code>
1277+ * columnName = ? AND columnName = ? AND columnName = ? ..."
1278+ * @throws SQLException if a database access error occurs
1279+ */
1280+ private String buildWhereClause(String whereClause,
1281+ ResultSet rs) throws SQLException {
1282+ whereClause = "WHERE ";
1283+
1284+ for (int i = 0; i < keyCols.length; i++) {
1285+ if (i > 0) {
1286+ whereClause += "AND ";
1287+ }
1288+ whereClause += callerMd.getColumnName(keyCols[i]);
1289+ params[i] = rs.getObject(keyCols[i]);
1290+ if (rs.wasNull() == true) {
1291+ whereClause += " IS NULL ";
1292+ } else {
1293+ whereClause += " = ? ";
1294+ }
1295+ }
1296+ return whereClause;
1297+ }
1298+
1299+ void updateResolvedConflictToDB(CachedRowSet crs, Connection con) throws SQLException {
1300+ //String updateExe = ;
1301+ PreparedStatement pStmt ;
1302+ String strWhere = "WHERE " ;
1303+ String strExec =" ";
1304+ String strUpdate = "UPDATE ";
1305+ int icolCount = crs.getMetaData().getColumnCount();
1306+ int keyColumns[] = crs.getKeyColumns();
1307+ Object param[];
1308+ String strSet="";
1309+
1310+ strWhere = buildWhereClause(strWhere, crs);
1311+
1312+ if (keyColumns == null || keyColumns.length == 0) {
1313+ keyColumns = new int[icolCount];
1314+ for (int i = 0; i < keyColumns.length; ) {
1315+ keyColumns[i] = ++i;
1316+ }
1317+ }
1318+ param = new Object[keyColumns.length];
1319+
1320+ strUpdate = "UPDATE " + buildTableName(con.getMetaData(),
1321+ crs.getMetaData().getCatalogName(1),
1322+ crs.getMetaData().getSchemaName(1),
1323+ crs.getTableName());
1324+
1325+ // changed or updated values will become part of
1326+ // set clause here
1327+ strUpdate += "SET ";
1328+
1329+ boolean first = true;
1330+
1331+ for (int i=1; i<=icolCount;i++) {
1332+ if (crs.columnUpdated(i)) {
1333+ if (first == false) {
1334+ strSet += ", ";
1335+ }
1336+ strSet += crs.getMetaData().getColumnName(i);
1337+ strSet += " = ? ";
1338+ first = false;
1339+ } //end if
1340+ } //end for
1341+
1342+ // keycols will become part of where clause
1343+ strUpdate += strSet;
1344+ strWhere = "WHERE ";
1345+
1346+ for (int i = 0; i < keyColumns.length; i++) {
1347+ if (i > 0) {
1348+ strWhere += "AND ";
1349+ }
1350+ strWhere += crs.getMetaData().getColumnName(keyColumns[i]);
1351+ param[i] = crs.getObject(keyColumns[i]);
1352+ if (crs.wasNull() == true) {
1353+ strWhere += " IS NULL ";
1354+ } else {
1355+ strWhere += " = ? ";
1356+ }
1357+ }
1358+ strUpdate += strWhere;
1359+
1360+ pStmt = con.prepareStatement(strUpdate);
1361+
1362+ int idx =0;
1363+ for (int i = 0; i < icolCount; i++) {
1364+ if(crs.columnUpdated(i+1)) {
1365+ Object obj = crs.getObject(i+1);
1366+ if (obj != null) {
1367+ pStmt.setObject(++idx, obj);
1368+ } else {
1369+ pStmt.setNull(i + 1,crs.getMetaData().getColumnType(i + 1));
1370+ } //end if ..else
1371+ } //end if crs.column...
1372+ } //end for
1373+
1374+ // Set the key cols for after WHERE =? clause
1375+ for (int i = 0; i < keyColumns.length; i++) {
1376+ if (param[i] != null) {
1377+ pStmt.setObject(++idx, param[i]);
1378+ }
1379+ }
1380+
1381+ int id = pStmt.executeUpdate();
1382+ }
1383+
1384+
1385+ /**
1386+ *
1387+ */
1388+ public void commit() throws SQLException {
1389+ con.commit();
1390+ if (reader.getCloseConnection() == true) {
1391+ con.close();
1392+ }
1393+ }
1394+
1395+ public void commit(CachedRowSetImpl crs, boolean updateRowset) throws SQLException {
1396+ con.commit();
1397+ if(updateRowset) {
1398+ if(crs.getCommand() != null)
1399+ crs.execute(con);
1400+ }
1401+
1402+ if (reader.getCloseConnection() == true) {
1403+ con.close();
1404+ }
1405+ }
1406+
1407+ /**
1408+ *
1409+ */
1410+ public void rollback() throws SQLException {
1411+ con.rollback();
1412+ if (reader.getCloseConnection() == true) {
1413+ con.close();
1414+ }
1415+ }
1416+
1417+ /**
1418+ *
1419+ */
1420+ public void rollback(Savepoint s) throws SQLException {
1421+ con.rollback(s);
1422+ if (reader.getCloseConnection() == true) {
1423+ con.close();
1424+ }
1425+ }
1426+
1427+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1428+ // Default state initialization happens here
1429+ ois.defaultReadObject();
1430+ // Initialization of Res Bundle happens here .
1431+ try {
1432+ resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
1433+ } catch(IOException ioe) {
1434+ throw new RuntimeException(ioe);
1435+ }
1436+
1437+ }
1438+
1439+ static final long serialVersionUID =-8506030970299413976L;
1440+}