/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.rowset.internal; import java.sql.*;import javax.sql.*;import java.util.*;import java.io.*; import com.sun.rowset.*;import java.text.MessageFormat;import javax.sql.rowset.*;import javax.sql.rowset.serial.SQLInputImpl;import javax.sql.rowset.serial.SerialArray;import javax.sql.rowset.serial.SerialBlob;import javax.sql.rowset.serial.SerialClob;import javax.sql.rowset.serial.SerialStruct;import javax.sql.rowset.spi.*; /** * The facility called on internally by the <code>RIOptimisticProvider</code> implementation to * propagate changes back to the data source from which the rowset got its data. * <P> * A <code>CachedRowSetWriter</code> object, called a writer, has the public * method <code>writeData</code> for writing modified data to the underlying data source. * This method is invoked by the rowset internally and is never invoked directly by an application. * A writer also has public methods for setting and getting * the <code>CachedRowSetReader</code> object, called a reader, that is associated * with the writer. The remainder of the methods in this class are private and * are invoked internally, either directly or indirectly, by the method * <code>writeData</code>. * <P> * Typically the <code>SyncFactory</code> manages the <code>RowSetReader</code> and * the <code>RowSetWriter</code> implementations using <code>SyncProvider</code> objects. * Standard JDBC RowSet implementations provide an object instance of this * writer by invoking the <code>SyncProvider.getRowSetWriter()</code> method. * * @version 0.2 * @author Jonathan Bruce * @see javax.sql.rowset.spi.SyncProvider * @see javax.sql.rowset.spi.SyncFactory * @see javax.sql.rowset.spi.SyncFactoryException */public class CachedRowSetWriter implements TransactionalWriter, Serializable { /** * The <code>Connection</code> object that this writer will use to make a * connection to the data source to which it will write data. * */ private transient Connection con; /** * The SQL <code>SELECT</code> command that this writer will call * internally. The method <code>initSQLStatements</code> builds this * command by supplying the words "SELECT" and "FROM," and using * metadata to get the table name and column names . * * @serial */ private String selectCmd; /** * The SQL <code>UPDATE</code> command that this writer will call * internally to write data to the rowset's underlying data source. * The method <code>initSQLStatements</code> builds this <code>String</code> * object. * * @serial */ private String updateCmd; /** * The SQL <code>WHERE</code> clause the writer will use for update * statements in the <code>PreparedStatement</code> object * it sends to the underlying data source. * * @serial */ private String updateWhere; /** * The SQL <code>DELETE</code> command that this writer will call * internally to delete a row in the rowset's underlying data source. * * @serial */ private String deleteCmd; /** * The SQL <code>WHERE</code> clause the writer will use for delete * statements in the <code>PreparedStatement</code> object * it sends to the underlying data source. * * @serial */ private String deleteWhere; /** * The SQL <code>INSERT INTO</code> command that this writer will internally use * to insert data into the rowset's underlying data source. The method * <code>initSQLStatements</code> builds this command with a question * mark parameter placeholder for each column in the rowset. * * @serial */ private String insertCmd; /** * An array containing the column numbers of the columns that are * needed to uniquely identify a row in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private int[] keyCols; /** * An array of the parameters that should be used to set the parameter * placeholders in a <code>PreparedStatement</code> object that this * writer will execute. * * @serial */ private Object[] params; /** * The <code>CachedRowSetReader</code> object that has been * set as the reader for the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private CachedRowSetReader reader; /** * The <code>ResultSetMetaData</code> object that contains information * about the columns in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private ResultSetMetaData callerMd; /** * The number of columns in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private int callerColumnCount; /** * This <code>CachedRowSet<code> will hold the conflicting values * retrieved from the db and hold it. */ private CachedRowSetImpl crsResolve; /** * This <code>ArrayList<code> will hold the values of SyncResolver.* */ private ArrayList status; /** * This will check whether the same field value has changed both * in database and CachedRowSet. */ private int iChangedValsInDbAndCRS; /** * This will hold the number of cols for which the values have * changed only in database. */ private int iChangedValsinDbOnly ; private JdbcRowSetResourceBundle resBundle; public CachedRowSetWriter() { try { resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } } /** * Propagates changes in the given <code>RowSet</code> object * back to its underlying data source and returns <code>true</code> * if successful. The writer will check to see if * the data in the pre-modified rowset (the original values) differ * from the data in the underlying data source. If data in the data * source has been modified by someone else, there is a conflict, * and in that case, the writer will not write to the data source. * In other words, the writer uses an optimistic concurrency algorithm: * It checks for conflicts before making changes rather than restricting * access for concurrent users. * <P> * This method is called by the rowset internally when * the application invokes the method <code>acceptChanges</code>. * The <code>writeData</code> method in turn calls private methods that * it defines internally. * The following is a general summary of what the method * <code>writeData</code> does, much of which is accomplished * through calls to its own internal methods. * <OL> * <LI>Creates a <code>CachedRowSet</code> object from the given * <code>RowSet</code> object * <LI>Makes a connection with the data source * <UL> * <LI>Disables autocommit mode if it is not already disabled * <LI>Sets the transaction isolation level to that of the rowset * </UL> * <LI>Checks to see if the reader has read new data since the writer * was last called and, if so, calls the method * <code>initSQLStatements</code> to initialize new SQL statements * <UL> * <LI>Builds new <code>SELECT</code>, <code>UPDATE</code>, * <code>INSERT</code>, and <code>DELETE</code> statements * <LI>Uses the <code>CachedRowSet</code> object's metadata to * determine the table name, column names, and the columns * that make up the primary key * </UL> * <LI>When there is no conflict, propagates changes made to the * <code>CachedRowSet</code> object back to its underlying data source * <UL> * <LI>Iterates through each row of the <code>CachedRowSet</code> object * to determine whether it has been updated, inserted, or deleted * <LI>If the corresponding row in the data source has not been changed * since the rowset last read its * values, the writer will use the appropriate command to update, * insert, or delete the row * <LI>If any data in the data source does not match the original values * for the <code>CachedRowSet</code> object, the writer will roll * back any changes it has made to the row in the data source. * </UL> * </OL> * * @return <code>true</code> if changes to the rowset were successfully * written to the rowset's underlying data source; * <code>false</code> otherwise */ public boolean writeData(RowSetInternal caller) throws SQLException { boolean conflict = false; boolean showDel = false; PreparedStatement pstmtIns = null; iChangedValsInDbAndCRS = 0; iChangedValsinDbOnly = 0; // We assume caller is a CachedRowSet CachedRowSetImpl crs = (CachedRowSetImpl)caller; // crsResolve = new CachedRowSetImpl(); this.crsResolve = new CachedRowSetImpl();; // The reader is registered with the writer at design time. // This is not required, in general. The reader has logic // to get a JDBC connection, so call it. con = reader.connect(caller); if (con == null) { throw new SQLException(resBundle.handleGetObject("crswriter.connect").toString()); } /* // Fix 6200646. // Don't change the connection or transaction properties. This will fail in a // J2EE container. if (con.getAutoCommit() == true) { con.setAutoCommit(false); } con.setTransactionIsolation(crs.getTransactionIsolation()); */ initSQLStatements(crs); int iColCount; RowSetMetaDataImpl rsmdWrite = (RowSetMetaDataImpl)crs.getMetaData(); RowSetMetaDataImpl rsmdResolv = new RowSetMetaDataImpl(); iColCount = rsmdWrite.getColumnCount(); int sz= crs.size()+1; status = new ArrayList(sz); status.add(0,null); rsmdResolv.setColumnCount(iColCount); for(int i =1; i <= iColCount; i++) { rsmdResolv.setColumnType(i, rsmdWrite.getColumnType(i)); rsmdResolv.setColumnName(i, rsmdWrite.getColumnName(i)); rsmdResolv.setNullable(i, ResultSetMetaData.columnNullableUnknown); } this.crsResolve.setMetaData(rsmdResolv); // moved outside the insert inner loop //pstmtIns = con.prepareStatement(insertCmd); if (callerColumnCount < 1) { // No data, so return success. if (reader.getCloseConnection() == true) con.close(); return true; } // We need to see rows marked for deletion. showDel = crs.getShowDeleted(); crs.setShowDeleted(true); // Look at all the rows. crs.beforeFirst(); int rows =1; while (crs.next()) { if (crs.rowDeleted()) { // The row has been deleted. if (conflict = (deleteOriginalRow(crs, this.crsResolve)) == true) { status.add(rows, Integer.valueOf(SyncResolver.DELETE_ROW_CONFLICT)); } else { // delete happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else if (crs.rowInserted()) { // The row has been inserted. pstmtIns = con.prepareStatement(insertCmd); if ( (conflict = insertNewRow(crs, pstmtIns, this.crsResolve)) == true) { status.add(rows, Integer.valueOf(SyncResolver.INSERT_ROW_CONFLICT)); } else { // insert happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else if (crs.rowUpdated()) { // The row has been updated. if ( conflict = (updateOriginalRow(crs)) == true) { status.add(rows, Integer.valueOf(SyncResolver.UPDATE_ROW_CONFLICT)); } else { // update happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else { /** The row is neither of inserted, updated or deleted. * So set nulls in the this.crsResolve for this row, * as nothing is to be done for such rows. * Also note that if such a row has been changed in database * and we have not changed(inserted, updated or deleted) * that is fine. **/ int icolCount = crs.getMetaData().getColumnCount(); status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); this.crsResolve.moveToInsertRow(); for(int cols=0;cols<iColCount;cols++) { this.crsResolve.updateNull(cols+1); } //end for this.crsResolve.insertRow(); this.crsResolve.moveToCurrentRow(); } //end if rows++; } //end while // close the insert statement if(pstmtIns!=null) pstmtIns.close(); // reset crs.setShowDeleted(showDel); boolean boolConf = false; for (int j=1;j<status.size();j++){ // ignore status for index = 0 which is set to null if(! ((status.get(j)).equals(Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)))) { // there is at least one conflict which needs to be resolved boolConf = true; break; } } crs.beforeFirst(); this.crsResolve.beforeFirst(); if(boolConf) { SyncProviderException spe = new SyncProviderException(status.size() - 1+resBundle.handleGetObject("crswriter.conflictsno").toString()); //SyncResolver syncRes = spe.getSyncResolver(); SyncResolverImpl syncResImpl = (SyncResolverImpl) spe.getSyncResolver(); syncResImpl.setCachedRowSet(crs); syncResImpl.setCachedRowSetResolver(this.crsResolve); syncResImpl.setStatus(status); syncResImpl.setCachedRowSetWriter(this); throw spe; } else { return true; } /* if (conflict == true) { con.rollback(); return false; } else { con.commit(); if (reader.getCloseConnection() == true) { con.close(); } return true; } */ } //end writeData /** * Updates the given <code>CachedRowSet</code> object's underlying data * source so that updates to the rowset are reflected in the original * data source, and returns <code>false</code> if the update was successful. * A return value of <code>true</code> indicates that there is a conflict, * meaning that a value updated in the rowset has already been changed by * someone else in the underlying data source. A conflict can also exist * if, for example, more than one row in the data source would be affected * by the update or if no rows would be affected. In any case, if there is * a conflict, this method does not update the underlying data source. * <P> * This method is called internally by the method <code>writeData</code> * if a row in the <code>CachedRowSet</code> object for which this * <code>CachedRowSetWriter</code> object is the writer has been updated. * * @return <code>false</code> if the update to the underlying data source is * successful; <code>true</code> otherwise * @throws SQLException if a database access error occurs */ private boolean updateOriginalRow(CachedRowSet crs) throws SQLException { PreparedStatement pstmt; int i = 0; int idx = 0; // Select the row from the database. ResultSet origVals = crs.getOriginalRow(); origVals.next(); try { updateWhere = buildWhereClause(updateWhere, origVals); /** * The following block of code is for checking a particular type of * query where in there is a where clause. Without this block, if a * SQL statement is built the "where" clause will appear twice hence * the DB errors out and a SQLException is thrown. This code also * considers that the where clause is in the right place as the * CachedRowSet object would already have been populated with this * query before coming to this point. **/ String tempselectCmd = selectCmd.toLowerCase(); int idxWhere = tempselectCmd.indexOf("where"); if(idxWhere != -1) { String tempSelect = selectCmd.substring(0,idxWhere); selectCmd = tempSelect; } pstmt = con.prepareStatement(selectCmd + updateWhere, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); for (i = 0; i < keyCols.length; i++) { if (params[i] != null) {Lines 1–500 of 1440. Load more
/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.rowset.internal; import java.sql.*; import javax.sql.*; import java.util.*; import java.io.*; import com.sun.rowset.*; import java.text.MessageFormat; import javax.sql.rowset.*; import javax.sql.rowset.serial.SQLInputImpl; import javax.sql.rowset.serial.SerialArray; import javax.sql.rowset.serial.SerialBlob; import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialStruct; import javax.sql.rowset.spi.*; /** * The facility called on internally by the <code>RIOptimisticProvider</code> implementation to * propagate changes back to the data source from which the rowset got its data. * <P> * A <code>CachedRowSetWriter</code> object, called a writer, has the public * method <code>writeData</code> for writing modified data to the underlying data source. * This method is invoked by the rowset internally and is never invoked directly by an application. * A writer also has public methods for setting and getting * the <code>CachedRowSetReader</code> object, called a reader, that is associated * with the writer. The remainder of the methods in this class are private and * are invoked internally, either directly or indirectly, by the method * <code>writeData</code>. * <P> * Typically the <code>SyncFactory</code> manages the <code>RowSetReader</code> and * the <code>RowSetWriter</code> implementations using <code>SyncProvider</code> objects. * Standard JDBC RowSet implementations provide an object instance of this * writer by invoking the <code>SyncProvider.getRowSetWriter()</code> method. * * @version 0.2 * @author Jonathan Bruce * @see javax.sql.rowset.spi.SyncProvider * @see javax.sql.rowset.spi.SyncFactory * @see javax.sql.rowset.spi.SyncFactoryException */ public class CachedRowSetWriter implements TransactionalWriter, Serializable { /** * The <code>Connection</code> object that this writer will use to make a * connection to the data source to which it will write data. * */ private transient Connection con; /** * The SQL <code>SELECT</code> command that this writer will call * internally. The method <code>initSQLStatements</code> builds this * command by supplying the words "SELECT" and "FROM," and using * metadata to get the table name and column names . * * @serial */ private String selectCmd; /** * The SQL <code>UPDATE</code> command that this writer will call * internally to write data to the rowset's underlying data source. * The method <code>initSQLStatements</code> builds this <code>String</code> * object. * * @serial */ private String updateCmd; /** * The SQL <code>WHERE</code> clause the writer will use for update * statements in the <code>PreparedStatement</code> object * it sends to the underlying data source. * * @serial */ private String updateWhere; /** * The SQL <code>DELETE</code> command that this writer will call * internally to delete a row in the rowset's underlying data source. * * @serial */ private String deleteCmd; /** * The SQL <code>WHERE</code> clause the writer will use for delete * statements in the <code>PreparedStatement</code> object * it sends to the underlying data source. * * @serial */ private String deleteWhere; /** * The SQL <code>INSERT INTO</code> command that this writer will internally use * to insert data into the rowset's underlying data source. The method * <code>initSQLStatements</code> builds this command with a question * mark parameter placeholder for each column in the rowset. * * @serial */ private String insertCmd; /** * An array containing the column numbers of the columns that are * needed to uniquely identify a row in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private int[] keyCols; /** * An array of the parameters that should be used to set the parameter * placeholders in a <code>PreparedStatement</code> object that this * writer will execute. * * @serial */ private Object[] params; /** * The <code>CachedRowSetReader</code> object that has been * set as the reader for the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private CachedRowSetReader reader; /** * The <code>ResultSetMetaData</code> object that contains information * about the columns in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private ResultSetMetaData callerMd; /** * The number of columns in the <code>CachedRowSet</code> object * for which this <code>CachedRowSetWriter</code> object is the writer. * * @serial */ private int callerColumnCount; /** * This <code>CachedRowSet<code> will hold the conflicting values * retrieved from the db and hold it. */ private CachedRowSetImpl crsResolve; /** * This <code>ArrayList<code> will hold the values of SyncResolver.* */ private ArrayList status; /** * This will check whether the same field value has changed both * in database and CachedRowSet. */ private int iChangedValsInDbAndCRS; /** * This will hold the number of cols for which the values have * changed only in database. */ private int iChangedValsinDbOnly ; private JdbcRowSetResourceBundle resBundle; public CachedRowSetWriter() { try { resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } } /** * Propagates changes in the given <code>RowSet</code> object * back to its underlying data source and returns <code>true</code> * if successful. The writer will check to see if * the data in the pre-modified rowset (the original values) differ * from the data in the underlying data source. If data in the data * source has been modified by someone else, there is a conflict, * and in that case, the writer will not write to the data source. * In other words, the writer uses an optimistic concurrency algorithm: * It checks for conflicts before making changes rather than restricting * access for concurrent users. * <P> * This method is called by the rowset internally when * the application invokes the method <code>acceptChanges</code>. * The <code>writeData</code> method in turn calls private methods that * it defines internally. * The following is a general summary of what the method * <code>writeData</code> does, much of which is accomplished * through calls to its own internal methods. * <OL> * <LI>Creates a <code>CachedRowSet</code> object from the given * <code>RowSet</code> object * <LI>Makes a connection with the data source * <UL> * <LI>Disables autocommit mode if it is not already disabled * <LI>Sets the transaction isolation level to that of the rowset * </UL> * <LI>Checks to see if the reader has read new data since the writer * was last called and, if so, calls the method * <code>initSQLStatements</code> to initialize new SQL statements * <UL> * <LI>Builds new <code>SELECT</code>, <code>UPDATE</code>, * <code>INSERT</code>, and <code>DELETE</code> statements * <LI>Uses the <code>CachedRowSet</code> object's metadata to * determine the table name, column names, and the columns * that make up the primary key * </UL> * <LI>When there is no conflict, propagates changes made to the * <code>CachedRowSet</code> object back to its underlying data source * <UL> * <LI>Iterates through each row of the <code>CachedRowSet</code> object * to determine whether it has been updated, inserted, or deleted * <LI>If the corresponding row in the data source has not been changed * since the rowset last read its * values, the writer will use the appropriate command to update, * insert, or delete the row * <LI>If any data in the data source does not match the original values * for the <code>CachedRowSet</code> object, the writer will roll * back any changes it has made to the row in the data source. * </UL> * </OL> * * @return <code>true</code> if changes to the rowset were successfully * written to the rowset's underlying data source; * <code>false</code> otherwise */ public boolean writeData(RowSetInternal caller) throws SQLException { boolean conflict = false; boolean showDel = false; PreparedStatement pstmtIns = null; iChangedValsInDbAndCRS = 0; iChangedValsinDbOnly = 0; // We assume caller is a CachedRowSet CachedRowSetImpl crs = (CachedRowSetImpl)caller; // crsResolve = new CachedRowSetImpl(); this.crsResolve = new CachedRowSetImpl();; // The reader is registered with the writer at design time. // This is not required, in general. The reader has logic // to get a JDBC connection, so call it. con = reader.connect(caller); if (con == null) { throw new SQLException(resBundle.handleGetObject("crswriter.connect").toString()); } /* // Fix 6200646. // Don't change the connection or transaction properties. This will fail in a // J2EE container. if (con.getAutoCommit() == true) { con.setAutoCommit(false); } con.setTransactionIsolation(crs.getTransactionIsolation()); */ initSQLStatements(crs); int iColCount; RowSetMetaDataImpl rsmdWrite = (RowSetMetaDataImpl)crs.getMetaData(); RowSetMetaDataImpl rsmdResolv = new RowSetMetaDataImpl(); iColCount = rsmdWrite.getColumnCount(); int sz= crs.size()+1; status = new ArrayList(sz); status.add(0,null); rsmdResolv.setColumnCount(iColCount); for(int i =1; i <= iColCount; i++) { rsmdResolv.setColumnType(i, rsmdWrite.getColumnType(i)); rsmdResolv.setColumnName(i, rsmdWrite.getColumnName(i)); rsmdResolv.setNullable(i, ResultSetMetaData.columnNullableUnknown); } this.crsResolve.setMetaData(rsmdResolv); // moved outside the insert inner loop //pstmtIns = con.prepareStatement(insertCmd); if (callerColumnCount < 1) { // No data, so return success. if (reader.getCloseConnection() == true) con.close(); return true; } // We need to see rows marked for deletion. showDel = crs.getShowDeleted(); crs.setShowDeleted(true); // Look at all the rows. crs.beforeFirst(); int rows =1; while (crs.next()) { if (crs.rowDeleted()) { // The row has been deleted. if (conflict = (deleteOriginalRow(crs, this.crsResolve)) == true) { status.add(rows, Integer.valueOf(SyncResolver.DELETE_ROW_CONFLICT)); } else { // delete happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else if (crs.rowInserted()) { // The row has been inserted. pstmtIns = con.prepareStatement(insertCmd); if ( (conflict = insertNewRow(crs, pstmtIns, this.crsResolve)) == true) { status.add(rows, Integer.valueOf(SyncResolver.INSERT_ROW_CONFLICT)); } else { // insert happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else if (crs.rowUpdated()) { // The row has been updated. if ( conflict = (updateOriginalRow(crs)) == true) { status.add(rows, Integer.valueOf(SyncResolver.UPDATE_ROW_CONFLICT)); } else { // update happened without any occurrence of conflicts // so update status accordingly status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); } } else { /** The row is neither of inserted, updated or deleted. * So set nulls in the this.crsResolve for this row, * as nothing is to be done for such rows. * Also note that if such a row has been changed in database * and we have not changed(inserted, updated or deleted) * that is fine. **/ int icolCount = crs.getMetaData().getColumnCount(); status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)); this.crsResolve.moveToInsertRow(); for(int cols=0;cols<iColCount;cols++) { this.crsResolve.updateNull(cols+1); } //end for this.crsResolve.insertRow(); this.crsResolve.moveToCurrentRow(); } //end if rows++; } //end while // close the insert statement if(pstmtIns!=null) pstmtIns.close(); // reset crs.setShowDeleted(showDel); boolean boolConf = false; for (int j=1;j<status.size();j++){ // ignore status for index = 0 which is set to null if(! ((status.get(j)).equals(Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)))) { // there is at least one conflict which needs to be resolved boolConf = true; break; } } crs.beforeFirst(); this.crsResolve.beforeFirst(); if(boolConf) { SyncProviderException spe = new SyncProviderException(status.size() - 1+resBundle.handleGetObject("crswriter.conflictsno").toString()); //SyncResolver syncRes = spe.getSyncResolver(); SyncResolverImpl syncResImpl = (SyncResolverImpl) spe.getSyncResolver(); syncResImpl.setCachedRowSet(crs); syncResImpl.setCachedRowSetResolver(this.crsResolve); syncResImpl.setStatus(status); syncResImpl.setCachedRowSetWriter(this); throw spe; } else { return true; } /* if (conflict == true) { con.rollback(); return false; } else { con.commit(); if (reader.getCloseConnection() == true) { con.close(); } return true; } */ } //end writeData /** * Updates the given <code>CachedRowSet</code> object's underlying data * source so that updates to the rowset are reflected in the original * data source, and returns <code>false</code> if the update was successful. * A return value of <code>true</code> indicates that there is a conflict, * meaning that a value updated in the rowset has already been changed by * someone else in the underlying data source. A conflict can also exist * if, for example, more than one row in the data source would be affected * by the update or if no rows would be affected. In any case, if there is * a conflict, this method does not update the underlying data source. * <P> * This method is called internally by the method <code>writeData</code> * if a row in the <code>CachedRowSet</code> object for which this * <code>CachedRowSetWriter</code> object is the writer has been updated. * * @return <code>false</code> if the update to the underlying data source is * successful; <code>true</code> otherwise * @throws SQLException if a database access error occurs */ private boolean updateOriginalRow(CachedRowSet crs) throws SQLException { PreparedStatement pstmt; int i = 0; int idx = 0; // Select the row from the database. ResultSet origVals = crs.getOriginalRow(); origVals.next(); try { updateWhere = buildWhereClause(updateWhere, origVals); /** * The following block of code is for checking a particular type of * query where in there is a where clause. Without this block, if a * SQL statement is built the "where" clause will appear twice hence * the DB errors out and a SQLException is thrown. This code also * considers that the where clause is in the right place as the * CachedRowSet object would already have been populated with this * query before coming to this point. **/ String tempselectCmd = selectCmd.toLowerCase(); int idxWhere = tempselectCmd.indexOf("where"); if(idxWhere != -1) { String tempSelect = selectCmd.substring(0,idxWhere); selectCmd = tempSelect; } pstmt = con.prepareStatement(selectCmd + updateWhere, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); for (i = 0; i < keyCols.length; i++) { if (params[i] != null) {