0N/A/*
2741N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.rowset.internal;
0N/A
0N/Aimport java.sql.*;
0N/Aimport javax.sql.*;
0N/Aimport java.util.*;
0N/Aimport java.io.*;
0N/A
0N/Aimport com.sun.rowset.*;
2741N/Aimport java.text.MessageFormat;
0N/Aimport javax.sql.rowset.*;
2741N/Aimport javax.sql.rowset.serial.SQLInputImpl;
2741N/Aimport javax.sql.rowset.serial.SerialArray;
2741N/Aimport javax.sql.rowset.serial.SerialBlob;
2741N/Aimport javax.sql.rowset.serial.SerialClob;
2741N/Aimport javax.sql.rowset.serial.SerialStruct;
0N/Aimport javax.sql.rowset.spi.*;
0N/A
0N/A
0N/A/**
0N/A * The facility called on internally by the <code>RIOptimisticProvider</code> implementation to
0N/A * propagate changes back to the data source from which the rowset got its data.
0N/A * <P>
0N/A * A <code>CachedRowSetWriter</code> object, called a writer, has the public
0N/A * method <code>writeData</code> for writing modified data to the underlying data source.
0N/A * This method is invoked by the rowset internally and is never invoked directly by an application.
0N/A * A writer also has public methods for setting and getting
0N/A * the <code>CachedRowSetReader</code> object, called a reader, that is associated
0N/A * with the writer. The remainder of the methods in this class are private and
0N/A * are invoked internally, either directly or indirectly, by the method
0N/A * <code>writeData</code>.
0N/A * <P>
0N/A * Typically the <code>SyncFactory</code> manages the <code>RowSetReader</code> and
0N/A * the <code>RowSetWriter</code> implementations using <code>SyncProvider</code> objects.
0N/A * Standard JDBC RowSet implementations provide an object instance of this
0N/A * writer by invoking the <code>SyncProvider.getRowSetWriter()</code> method.
0N/A *
2741N/A * @version 0.2
0N/A * @author Jonathan Bruce
0N/A * @see javax.sql.rowset.spi.SyncProvider
0N/A * @see javax.sql.rowset.spi.SyncFactory
0N/A * @see javax.sql.rowset.spi.SyncFactoryException
0N/A */
0N/Apublic class CachedRowSetWriter implements TransactionalWriter, Serializable {
0N/A
0N/A/**
0N/A * The <code>Connection</code> object that this writer will use to make a
0N/A * connection to the data source to which it will write data.
0N/A *
0N/A */
0N/A private transient Connection con;
0N/A
0N/A/**
0N/A * The SQL <code>SELECT</code> command that this writer will call
0N/A * internally. The method <code>initSQLStatements</code> builds this
0N/A * command by supplying the words "SELECT" and "FROM," and using
0N/A * metadata to get the table name and column names .
0N/A *
0N/A * @serial
0N/A */
0N/A private String selectCmd;
0N/A
0N/A/**
0N/A * The SQL <code>UPDATE</code> command that this writer will call
0N/A * internally to write data to the rowset's underlying data source.
0N/A * The method <code>initSQLStatements</code> builds this <code>String</code>
0N/A * object.
0N/A *
0N/A * @serial
0N/A */
0N/A private String updateCmd;
0N/A
0N/A/**
0N/A * The SQL <code>WHERE</code> clause the writer will use for update
0N/A * statements in the <code>PreparedStatement</code> object
0N/A * it sends to the underlying data source.
0N/A *
0N/A * @serial
0N/A */
0N/A private String updateWhere;
0N/A
0N/A/**
0N/A * The SQL <code>DELETE</code> command that this writer will call
0N/A * internally to delete a row in the rowset's underlying data source.
0N/A *
0N/A * @serial
0N/A */
0N/A private String deleteCmd;
0N/A
0N/A/**
0N/A * The SQL <code>WHERE</code> clause the writer will use for delete
0N/A * statements in the <code>PreparedStatement</code> object
0N/A * it sends to the underlying data source.
0N/A *
0N/A * @serial
0N/A */
0N/A private String deleteWhere;
0N/A
0N/A/**
0N/A * The SQL <code>INSERT INTO</code> command that this writer will internally use
0N/A * to insert data into the rowset's underlying data source. The method
0N/A * <code>initSQLStatements</code> builds this command with a question
0N/A * mark parameter placeholder for each column in the rowset.
0N/A *
0N/A * @serial
0N/A */
0N/A private String insertCmd;
0N/A
0N/A/**
0N/A * An array containing the column numbers of the columns that are
0N/A * needed to uniquely identify a row in the <code>CachedRowSet</code> object
0N/A * for which this <code>CachedRowSetWriter</code> object is the writer.
0N/A *
0N/A * @serial
0N/A */
0N/A private int[] keyCols;
0N/A
0N/A/**
0N/A * An array of the parameters that should be used to set the parameter
0N/A * placeholders in a <code>PreparedStatement</code> object that this
0N/A * writer will execute.
0N/A *
0N/A * @serial
0N/A */
0N/A private Object[] params;
0N/A
0N/A/**
0N/A * The <code>CachedRowSetReader</code> object that has been
0N/A * set as the reader for the <code>CachedRowSet</code> object
0N/A * for which this <code>CachedRowSetWriter</code> object is the writer.
0N/A *
0N/A * @serial
0N/A */
0N/A private CachedRowSetReader reader;
0N/A
0N/A/**
0N/A * The <code>ResultSetMetaData</code> object that contains information
0N/A * about the columns in the <code>CachedRowSet</code> object
0N/A * for which this <code>CachedRowSetWriter</code> object is the writer.
0N/A *
0N/A * @serial
0N/A */
0N/A private ResultSetMetaData callerMd;
0N/A
0N/A/**
0N/A * The number of columns in the <code>CachedRowSet</code> object
0N/A * for which this <code>CachedRowSetWriter</code> object is the writer.
0N/A *
0N/A * @serial
0N/A */
0N/A private int callerColumnCount;
0N/A
0N/A/**
0N/A * This <code>CachedRowSet<code> will hold the conflicting values
0N/A * retrieved from the db and hold it.
0N/A */
0N/A private CachedRowSetImpl crsResolve;
0N/A
0N/A/**
0N/A * This <code>ArrayList<code> will hold the values of SyncResolver.*
0N/A */
0N/A private ArrayList status;
0N/A
0N/A/**
0N/A * This will check whether the same field value has changed both
0N/A * in database and CachedRowSet.
0N/A */
0N/A private int iChangedValsInDbAndCRS;
0N/A
0N/A/**
0N/A * This will hold the number of cols for which the values have
0N/A * changed only in database.
0N/A */
0N/A private int iChangedValsinDbOnly ;
0N/A
0N/A private JdbcRowSetResourceBundle resBundle;
0N/A
0N/A public CachedRowSetWriter() {
0N/A try {
0N/A resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
0N/A } catch(IOException ioe) {
0N/A throw new RuntimeException(ioe);
0N/A }
0N/A }
0N/A
0N/A/**
0N/A * Propagates changes in the given <code>RowSet</code> object
0N/A * back to its underlying data source and returns <code>true</code>
0N/A * if successful. The writer will check to see if
0N/A * the data in the pre-modified rowset (the original values) differ
0N/A * from the data in the underlying data source. If data in the data
0N/A * source has been modified by someone else, there is a conflict,
0N/A * and in that case, the writer will not write to the data source.
0N/A * In other words, the writer uses an optimistic concurrency algorithm:
0N/A * It checks for conflicts before making changes rather than restricting
0N/A * access for concurrent users.
0N/A * <P>
0N/A * This method is called by the rowset internally when
0N/A * the application invokes the method <code>acceptChanges</code>.
0N/A * The <code>writeData</code> method in turn calls private methods that
0N/A * it defines internally.
0N/A * The following is a general summary of what the method
0N/A * <code>writeData</code> does, much of which is accomplished
0N/A * through calls to its own internal methods.
0N/A * <OL>
0N/A * <LI>Creates a <code>CachedRowSet</code> object from the given
0N/A * <code>RowSet</code> object
0N/A * <LI>Makes a connection with the data source
0N/A * <UL>
0N/A * <LI>Disables autocommit mode if it is not already disabled
0N/A * <LI>Sets the transaction isolation level to that of the rowset
0N/A * </UL>
0N/A * <LI>Checks to see if the reader has read new data since the writer
0N/A * was last called and, if so, calls the method
0N/A * <code>initSQLStatements</code> to initialize new SQL statements
0N/A * <UL>
0N/A * <LI>Builds new <code>SELECT</code>, <code>UPDATE</code>,
0N/A * <code>INSERT</code>, and <code>DELETE</code> statements
0N/A * <LI>Uses the <code>CachedRowSet</code> object's metadata to
0N/A * determine the table name, column names, and the columns
0N/A * that make up the primary key
0N/A * </UL>
0N/A * <LI>When there is no conflict, propagates changes made to the
0N/A * <code>CachedRowSet</code> object back to its underlying data source
0N/A * <UL>
0N/A * <LI>Iterates through each row of the <code>CachedRowSet</code> object
0N/A * to determine whether it has been updated, inserted, or deleted
0N/A * <LI>If the corresponding row in the data source has not been changed
0N/A * since the rowset last read its
0N/A * values, the writer will use the appropriate command to update,
0N/A * insert, or delete the row
0N/A * <LI>If any data in the data source does not match the original values
0N/A * for the <code>CachedRowSet</code> object, the writer will roll
0N/A * back any changes it has made to the row in the data source.
0N/A * </UL>
0N/A * </OL>
0N/A *
0N/A * @return <code>true</code> if changes to the rowset were successfully
0N/A * written to the rowset's underlying data source;
0N/A * <code>false</code> otherwise
0N/A */
0N/A public boolean writeData(RowSetInternal caller) throws SQLException {
0N/A boolean conflict = false;
0N/A boolean showDel = false;
0N/A PreparedStatement pstmtIns = null;
0N/A iChangedValsInDbAndCRS = 0;
0N/A iChangedValsinDbOnly = 0;
0N/A
0N/A // We assume caller is a CachedRowSet
0N/A CachedRowSetImpl crs = (CachedRowSetImpl)caller;
0N/A // crsResolve = new CachedRowSetImpl();
0N/A this.crsResolve = new CachedRowSetImpl();;
0N/A
0N/A // The reader is registered with the writer at design time.
0N/A // This is not required, in general. The reader has logic
0N/A // to get a JDBC connection, so call it.
0N/A
0N/A con = reader.connect(caller);
0N/A
0N/A
0N/A if (con == null) {
0N/A throw new SQLException(resBundle.handleGetObject("crswriter.connect").toString());
0N/A }
0N/A
0N/A /*
0N/A // Fix 6200646.
0N/A // Don't change the connection or transaction properties. This will fail in a
0N/A // J2EE container.
0N/A if (con.getAutoCommit() == true) {
0N/A con.setAutoCommit(false);
0N/A }
0N/A
0N/A con.setTransactionIsolation(crs.getTransactionIsolation());
0N/A */
0N/A
0N/A initSQLStatements(crs);
0N/A int iColCount;
0N/A
0N/A RowSetMetaDataImpl rsmdWrite = (RowSetMetaDataImpl)crs.getMetaData();
0N/A RowSetMetaDataImpl rsmdResolv = new RowSetMetaDataImpl();
0N/A
0N/A iColCount = rsmdWrite.getColumnCount();
0N/A int sz= crs.size()+1;
0N/A status = new ArrayList(sz);
0N/A
0N/A status.add(0,null);
0N/A rsmdResolv.setColumnCount(iColCount);
0N/A
0N/A for(int i =1; i <= iColCount; i++) {
0N/A rsmdResolv.setColumnType(i, rsmdWrite.getColumnType(i));
0N/A rsmdResolv.setColumnName(i, rsmdWrite.getColumnName(i));
0N/A rsmdResolv.setNullable(i, ResultSetMetaData.columnNullableUnknown);
0N/A }
0N/A this.crsResolve.setMetaData(rsmdResolv);
0N/A
0N/A // moved outside the insert inner loop
0N/A //pstmtIns = con.prepareStatement(insertCmd);
0N/A
0N/A if (callerColumnCount < 1) {
0N/A // No data, so return success.
0N/A if (reader.getCloseConnection() == true)
0N/A con.close();
0N/A return true;
0N/A }
0N/A // We need to see rows marked for deletion.
0N/A showDel = crs.getShowDeleted();
0N/A crs.setShowDeleted(true);
0N/A
0N/A // Look at all the rows.
0N/A crs.beforeFirst();
0N/A
0N/A int rows =1;
0N/A while (crs.next()) {
0N/A if (crs.rowDeleted()) {
0N/A // The row has been deleted.
0N/A if (conflict = (deleteOriginalRow(crs, this.crsResolve)) == true) {
2828N/A status.add(rows, Integer.valueOf(SyncResolver.DELETE_ROW_CONFLICT));
0N/A } else {
0N/A // delete happened without any occurrence of conflicts
0N/A // so update status accordingly
2828N/A status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
0N/A }
0N/A
0N/A } else if (crs.rowInserted()) {
0N/A // The row has been inserted.
0N/A
0N/A pstmtIns = con.prepareStatement(insertCmd);
0N/A if ( (conflict = insertNewRow(crs, pstmtIns, this.crsResolve)) == true) {
2828N/A status.add(rows, Integer.valueOf(SyncResolver.INSERT_ROW_CONFLICT));
0N/A } else {
0N/A // insert happened without any occurrence of conflicts
0N/A // so update status accordingly
2828N/A status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
0N/A }
0N/A } else if (crs.rowUpdated()) {
0N/A // The row has been updated.
0N/A if ( conflict = (updateOriginalRow(crs)) == true) {
2828N/A status.add(rows, Integer.valueOf(SyncResolver.UPDATE_ROW_CONFLICT));
0N/A } else {
0N/A // update happened without any occurrence of conflicts
0N/A // so update status accordingly
2828N/A status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
0N/A }
0N/A
0N/A } else {
0N/A /** The row is neither of inserted, updated or deleted.
0N/A * So set nulls in the this.crsResolve for this row,
0N/A * as nothing is to be done for such rows.
0N/A * Also note that if such a row has been changed in database
0N/A * and we have not changed(inserted, updated or deleted)
0N/A * that is fine.
0N/A **/
0N/A int icolCount = crs.getMetaData().getColumnCount();
2828N/A status.add(rows, Integer.valueOf(SyncResolver.NO_ROW_CONFLICT));
0N/A
0N/A this.crsResolve.moveToInsertRow();
0N/A for(int cols=0;cols<iColCount;cols++) {
0N/A this.crsResolve.updateNull(cols+1);
0N/A } //end for
0N/A
0N/A this.crsResolve.insertRow();
0N/A this.crsResolve.moveToCurrentRow();
0N/A
0N/A } //end if
0N/A rows++;
0N/A } //end while
0N/A
0N/A // close the insert statement
0N/A if(pstmtIns!=null)
0N/A pstmtIns.close();
0N/A // reset
0N/A crs.setShowDeleted(showDel);
0N/A
0N/A boolean boolConf = false;
0N/A for (int j=1;j<status.size();j++){
0N/A // ignore status for index = 0 which is set to null
2828N/A if(! ((status.get(j)).equals(Integer.valueOf(SyncResolver.NO_ROW_CONFLICT)))) {
0N/A // there is at least one conflict which needs to be resolved
0N/A boolConf = true;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A crs.beforeFirst();
0N/A this.crsResolve.beforeFirst();
0N/A
0N/A if(boolConf) {
0N/A SyncProviderException spe = new SyncProviderException(status.size() - 1+resBundle.handleGetObject("crswriter.conflictsno").toString());
0N/A //SyncResolver syncRes = spe.getSyncResolver();
0N/A
0N/A SyncResolverImpl syncResImpl = (SyncResolverImpl) spe.getSyncResolver();
0N/A
0N/A syncResImpl.setCachedRowSet(crs);
0N/A syncResImpl.setCachedRowSetResolver(this.crsResolve);
0N/A
0N/A syncResImpl.setStatus(status);
0N/A syncResImpl.setCachedRowSetWriter(this);
0N/A
0N/A throw spe;
0N/A } else {
0N/A return true;
0N/A }
0N/A /*
0N/A if (conflict == true) {
0N/A con.rollback();
0N/A return false;
0N/A } else {
0N/A con.commit();
0N/A if (reader.getCloseConnection() == true) {
0N/A con.close();
0N/A }
0N/A return true;
0N/A }
0N/A */
0N/A
0N/A } //end writeData
0N/A
0N/A/**
0N/A * Updates the given <code>CachedRowSet</code> object's underlying data
0N/A * source so that updates to the rowset are reflected in the original
0N/A * data source, and returns <code>false</code> if the update was successful.
0N/A * A return value of <code>true</code> indicates that there is a conflict,
0N/A * meaning that a value updated in the rowset has already been changed by
0N/A * someone else in the underlying data source. A conflict can also exist
0N/A * if, for example, more than one row in the data source would be affected
0N/A * by the update or if no rows would be affected. In any case, if there is
0N/A * a conflict, this method does not update the underlying data source.
0N/A * <P>
0N/A * This method is called internally by the method <code>writeData</code>
0N/A * if a row in the <code>CachedRowSet</code> object for which this
0N/A * <code>CachedRowSetWriter</code> object is the writer has been updated.
0N/A *
0N/A * @return <code>false</code> if the update to the underlying data source is
0N/A * successful; <code>true</code> otherwise
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private boolean updateOriginalRow(CachedRowSet crs)
0N/A throws SQLException {
0N/A PreparedStatement pstmt;
0N/A int i = 0;
0N/A int idx = 0;
0N/A
0N/A // Select the row from the database.
0N/A ResultSet origVals = crs.getOriginalRow();
0N/A origVals.next();
0N/A
0N/A try {
0N/A updateWhere = buildWhereClause(updateWhere, origVals);
0N/A
0N/A
0N/A /**
0N/A * The following block of code is for checking a particular type of
0N/A * query where in there is a where clause. Without this block, if a
0N/A * SQL statement is built the "where" clause will appear twice hence
0N/A * the DB errors out and a SQLException is thrown. This code also
0N/A * considers that the where clause is in the right place as the
0N/A * CachedRowSet object would already have been populated with this
0N/A * query before coming to this point.
0N/A **/
0N/A
0N/A
0N/A String tempselectCmd = selectCmd.toLowerCase();
0N/A
0N/A int idxWhere = tempselectCmd.indexOf("where");
0N/A
0N/A if(idxWhere != -1)
0N/A {
0N/A String tempSelect = selectCmd.substring(0,idxWhere);
0N/A selectCmd = tempSelect;
0N/A }
0N/A
0N/A pstmt = con.prepareStatement(selectCmd + updateWhere,
0N/A ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
0N/A
0N/A for (i = 0; i < keyCols.length; i++) {
0N/A if (params[i] != null) {
0N/A pstmt.setObject(++idx, params[i]);
0N/A } else {
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A try {
0N/A pstmt.setMaxRows(crs.getMaxRows());
0N/A pstmt.setMaxFieldSize(crs.getMaxFieldSize());
0N/A pstmt.setEscapeProcessing(crs.getEscapeProcessing());
0N/A pstmt.setQueryTimeout(crs.getQueryTimeout());
0N/A } catch (Exception ex) {
0N/A // Older driver don't support these operations.
0N/A }
0N/A
0N/A ResultSet rs = null;
0N/A rs = pstmt.executeQuery();
2741N/A ResultSetMetaData rsmd = rs.getMetaData();
0N/A
2741N/A if (rs.next()) {
0N/A if (rs.next()) {
2741N/A /** More than one row conflict.
0N/A * If rs has only one row we are able to
0N/A * uniquely identify the row where update
0N/A * have to happen else if more than one
0N/A * row implies we cannot uniquely identify the row
0N/A * where we have to do updates.
0N/A * crs.setKeyColumns needs to be set to
0N/A * come out of this situation.
0N/A */
0N/A
0N/A return true;
0N/A }
0N/A
0N/A // don't close the rs
0N/A // we require the record in rs to be used.
0N/A // rs.close();
0N/A // pstmt.close();
2741N/A rs.first();
0N/A
0N/A // how many fields need to be updated
0N/A int colsNotChanged = 0;
0N/A Vector cols = new Vector();
2823N/A String updateExec = updateCmd;
0N/A Object orig;
0N/A Object curr;
0N/A Object rsval;
0N/A boolean boolNull = true;
0N/A Object objVal = null;
0N/A
0N/A // There's only one row and the cursor
0N/A // needs to be on that row.
0N/A
0N/A boolean first = true;
0N/A boolean flag = true;
0N/A
0N/A this.crsResolve.moveToInsertRow();
0N/A
0N/A for (i = 1; i <= callerColumnCount; i++) {
0N/A orig = origVals.getObject(i);
0N/A curr = crs.getObject(i);
0N/A rsval = rs.getObject(i);
2741N/A /*
2741N/A * the following block creates equivalent objects
2741N/A * that would have been created if this rs is populated
2741N/A * into a CachedRowSet so that comparison of the column values
2741N/A * from the ResultSet and CachedRowSet are possible
2741N/A */
2741N/A Map map = (crs.getTypeMap() == null)?con.getTypeMap():crs.getTypeMap();
2741N/A if (rsval instanceof Struct) {
2741N/A
2741N/A Struct s = (Struct)rsval;
2741N/A
2741N/A // look up the class in the map
2741N/A Class c = null;
2741N/A c = (Class)map.get(s.getSQLTypeName());
2741N/A if (c != null) {
2741N/A // create new instance of the class
2741N/A SQLData obj = null;
2741N/A try {
2741N/A obj = (SQLData)c.newInstance();
2741N/A } catch (java.lang.InstantiationException ex) {
2741N/A throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
2741N/A ex.getMessage()));
2741N/A } catch (java.lang.IllegalAccessException ex) {
2741N/A throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(),
2741N/A ex.getMessage()));
2741N/A }
2741N/A // get the attributes from the struct
2741N/A Object attribs[] = s.getAttributes(map);
2741N/A // create the SQLInput "stream"
2741N/A SQLInputImpl sqlInput = new SQLInputImpl(attribs, map);
2741N/A // read the values...
2741N/A obj.readSQL(sqlInput, s.getSQLTypeName());
2741N/A rsval = obj;
2741N/A }
2741N/A } else if (rsval instanceof SQLData) {
2741N/A rsval = new SerialStruct((SQLData)rsval, map);
2741N/A } else if (rsval instanceof Blob) {
2741N/A rsval = new SerialBlob((Blob)rsval);
2741N/A } else if (rsval instanceof Clob) {
2741N/A rsval = new SerialClob((Clob)rsval);
2741N/A } else if (rsval instanceof java.sql.Array) {
2741N/A rsval = new SerialArray((java.sql.Array)rsval, map);
2741N/A }
0N/A
0N/A // reset boolNull if it had been set
0N/A boolNull = true;
0N/A
0N/A /** This addtional checking has been added when the current value
0N/A * in the DB is null, but the DB had a different value when the
0N/A * data was actaully fetched into the CachedRowSet.
0N/A **/
0N/A
0N/A if(rsval == null && orig != null) {
0N/A // value in db has changed
0N/A // don't proceed with synchronization
0N/A // get the value in db and pass it to the resolver.
0N/A
0N/A iChangedValsinDbOnly++;
0N/A // Set the boolNull to false,
0N/A // in order to set the actual value;
0N/A boolNull = false;
0N/A objVal = rsval;
0N/A }
0N/A
0N/A /** Adding the checking for rsval to be "not" null or else
0N/A * it would through a NullPointerException when the values
0N/A * are compared.
0N/A **/
0N/A
0N/A else if(rsval != null && (!rsval.equals(orig)))
0N/A {
0N/A // value in db has changed
0N/A // don't proceed with synchronization
0N/A // get the value in db and pass it to the resolver.
0N/A
0N/A iChangedValsinDbOnly++;
0N/A // Set the boolNull to false,
0N/A // in order to set the actual value;
0N/A boolNull = false;
0N/A objVal = rsval;
0N/A } else if ( (orig == null || curr == null) ) {
0N/A
0N/A /** Adding the additonal condition of checking for "flag"
0N/A * boolean variable, which would otherwise result in
0N/A * building a invalid query, as the comma would not be
0N/A * added to the query string.
0N/A **/
0N/A
0N/A if (first == false || flag == false) {
0N/A updateExec += ", ";
0N/A }
0N/A updateExec += crs.getMetaData().getColumnName(i);
2828N/A cols.add(Integer.valueOf(i));
0N/A updateExec += " = ? ";
0N/A first = false;
0N/A
0N/A /** Adding the extra condition for orig to be "not" null as the
0N/A * condition for orig to be null is take prior to this, if this
0N/A * is not added it will result in a NullPointerException when
0N/A * the values are compared.
0N/A **/
0N/A
0N/A } else if (orig.equals(curr)) {
0N/A colsNotChanged++;
0N/A //nothing to update in this case since values are equal
0N/A
0N/A /** Adding the extra condition for orig to be "not" null as the
0N/A * condition for orig to be null is take prior to this, if this
0N/A * is not added it will result in a NullPointerException when
0N/A * the values are compared.
0N/A **/
0N/A
0N/A } else if(orig.equals(curr) == false) {
0N/A // When values from db and values in CachedRowSet are not equal,
0N/A // if db value is same as before updation for each col in
0N/A // the row before fetching into CachedRowSet,
0N/A // only then we go ahead with updation, else we
0N/A // throw SyncProviderException.
0N/A
0N/A // if value has changed in db after fetching from db
0N/A // for some cols of the row and at the same time, some other cols
0N/A // have changed in CachedRowSet, no synchronization happens
0N/A
0N/A // Synchronization happens only when data when fetching is
0N/A // same or at most has changed in cachedrowset
0N/A
0N/A // check orig value with what is there in crs for a column
0N/A // before updation in crs.
0N/A
0N/A if(crs.columnUpdated(i)) {
0N/A if(rsval.equals(orig)) {
0N/A // At this point we are sure that
0N/A // the value updated in crs was from
0N/A // what is in db now and has not changed
0N/A if (flag == false || first == false) {
0N/A updateExec += ", ";
0N/A }
0N/A updateExec += crs.getMetaData().getColumnName(i);
2828N/A cols.add(Integer.valueOf(i));
0N/A updateExec += " = ? ";
0N/A flag = false;
0N/A } else {
0N/A // Here the value has changed in the db after
0N/A // data was fetched
0N/A // Plus store this row from CachedRowSet and keep it
0N/A // in a new CachedRowSet
0N/A boolNull= false;
0N/A objVal = rsval;
0N/A iChangedValsInDbAndCRS++;
0N/A }
0N/A }
0N/A }
0N/A
0N/A if(!boolNull) {
0N/A this.crsResolve.updateObject(i,objVal);
0N/A } else {
0N/A this.crsResolve.updateNull(i);
0N/A }
0N/A } //end for
0N/A
2741N/A rs.close();
2741N/A pstmt.close();
2741N/A
0N/A this.crsResolve.insertRow();
0N/A this.crsResolve.moveToCurrentRow();
0N/A
0N/A /**
0N/A * if nothing has changed return now - this can happen
0N/A * if column is updated to the same value.
0N/A * if colsNotChanged == callerColumnCount implies we are updating
0N/A * the database with ALL COLUMNS HAVING SAME VALUES,
0N/A * so skip going to database, else do as usual.
0N/A **/
0N/A if ( (first == false && cols.size() == 0) ||
0N/A colsNotChanged == callerColumnCount ) {
0N/A return false;
0N/A }
0N/A
0N/A if(iChangedValsInDbAndCRS != 0 || iChangedValsinDbOnly != 0) {
0N/A return true;
0N/A }
0N/A
0N/A
0N/A updateExec += updateWhere;
0N/A
0N/A pstmt = con.prepareStatement(updateExec);
0N/A
0N/A // Comments needed here
0N/A for (i = 0; i < cols.size(); i++) {
0N/A Object obj = crs.getObject(((Integer)cols.get(i)).intValue());
0N/A if (obj != null)
0N/A pstmt.setObject(i + 1, obj);
0N/A else
0N/A pstmt.setNull(i + 1,crs.getMetaData().getColumnType(i + 1));
0N/A }
0N/A idx = i;
0N/A
0N/A // Comments needed here
0N/A for (i = 0; i < keyCols.length; i++) {
0N/A if (params[i] != null) {
0N/A pstmt.setObject(++idx, params[i]);
0N/A } else {
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A i = pstmt.executeUpdate();
0N/A
0N/A /**
0N/A * i should be equal to 1(row count), because we update
0N/A * one row(returned as row count) at a time, if all goes well.
0N/A * if 1 != 1, this implies we have not been able to
0N/A * do updations properly i.e there is a conflict in database
0N/A * versus what is in CachedRowSet for this particular row.
0N/A **/
0N/A
0N/A return false;
0N/A
0N/A } else {
0N/A /**
0N/A * Cursor will be here, if the ResultSet may not return even a single row
0N/A * i.e. we can't find the row where to update because it has been deleted
0N/A * etc. from the db.
0N/A * Present the whole row as null to user, to force null to be sync'ed
0N/A * and hence nothing to be synced.
0N/A *
0N/A * NOTE:
0N/A * ------
0N/A * In the database if a column that is mapped to java.sql.Types.REAL stores
0N/A * a Double value and is compared with value got from ResultSet.getFloat()
0N/A * no row is retrieved and will throw a SyncProviderException. For details
0N/A * see bug Id 5053830
0N/A **/
0N/A return true;
0N/A }
0N/A } catch (SQLException ex) {
0N/A ex.printStackTrace();
0N/A // if executeUpdate fails it will come here,
0N/A // update crsResolve with null rows
0N/A this.crsResolve.moveToInsertRow();
0N/A
0N/A for(i = 1; i <= callerColumnCount; i++) {
0N/A this.crsResolve.updateNull(i);
0N/A }
0N/A
0N/A this.crsResolve.insertRow();
0N/A this.crsResolve.moveToCurrentRow();
0N/A
0N/A return true;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Inserts a row that has been inserted into the given
0N/A * <code>CachedRowSet</code> object into the data source from which
0N/A * the rowset is derived, returning <code>false</code> if the insertion
0N/A * was successful.
0N/A *
0N/A * @param crs the <code>CachedRowSet</code> object that has had a row inserted
0N/A * and to whose underlying data source the row will be inserted
0N/A * @param pstmt the <code>PreparedStatement</code> object that will be used
0N/A * to execute the insertion
0N/A * @return <code>false</code> to indicate that the insertion was successful;
0N/A * <code>true</code> otherwise
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private boolean insertNewRow(CachedRowSet crs,
0N/A PreparedStatement pstmt, CachedRowSetImpl crsRes) throws SQLException {
0N/A int i = 0;
0N/A int icolCount = crs.getMetaData().getColumnCount();
0N/A
0N/A boolean returnVal = false;
0N/A PreparedStatement pstmtSel = con.prepareStatement(selectCmd,
0N/A ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
0N/A ResultSet rs, rs2 = null;
0N/A DatabaseMetaData dbmd = con.getMetaData();
0N/A rs = pstmtSel.executeQuery();
0N/A String table = crs.getTableName();
0N/A rs2 = dbmd.getPrimaryKeys(null, null, table);
0N/A String [] primaryKeys = new String[icolCount];
0N/A int k = 0;
0N/A while(rs2.next()) {
0N/A String pkcolname = rs2.getString("COLUMN_NAME");
0N/A primaryKeys[k] = pkcolname;
0N/A k++;
0N/A }
0N/A
0N/A if(rs.next()) {
0N/A for(int j=0;j<primaryKeys.length;j++) {
0N/A if(primaryKeys[j] != null) {
0N/A if(crs.getObject(primaryKeys[j]) == null){
0N/A break;
0N/A }
0N/A String crsPK = (crs.getObject(primaryKeys[j])).toString();
0N/A String rsPK = (rs.getObject(primaryKeys[j])).toString();
0N/A if(crsPK.equals(rsPK)) {
0N/A returnVal = true;
0N/A this.crsResolve.moveToInsertRow();
0N/A for(i = 1; i <= icolCount; i++) {
0N/A String colname = (rs.getMetaData()).getColumnName(i);
0N/A if(colname.equals(primaryKeys[j]))
0N/A this.crsResolve.updateObject(i,rsPK);
0N/A else
0N/A this.crsResolve.updateNull(i);
0N/A }
0N/A this.crsResolve.insertRow();
0N/A this.crsResolve.moveToCurrentRow();
0N/A }
0N/A }
0N/A }
0N/A }
0N/A if(returnVal)
0N/A return returnVal;
0N/A
0N/A try {
0N/A for (i = 1; i <= icolCount; i++) {
0N/A Object obj = crs.getObject(i);
0N/A if (obj != null) {
0N/A pstmt.setObject(i, obj);
0N/A } else {
0N/A pstmt.setNull(i,crs.getMetaData().getColumnType(i));
0N/A }
0N/A }
0N/A
0N/A i = pstmt.executeUpdate();
0N/A return false;
0N/A
0N/A } catch (SQLException ex) {
0N/A /**
0N/A * Cursor will come here if executeUpdate fails.
0N/A * There can be many reasons why the insertion failed,
0N/A * one can be violation of primary key.
0N/A * Hence we cannot exactly identify why the insertion failed
0N/A * Present the current row as a null row to the user.
0N/A **/
0N/A this.crsResolve.moveToInsertRow();
0N/A
0N/A for(i = 1; i <= icolCount; i++) {
0N/A this.crsResolve.updateNull(i);
0N/A }
0N/A
0N/A this.crsResolve.insertRow();
0N/A this.crsResolve.moveToCurrentRow();
0N/A
0N/A return true;
0N/A }
0N/A }
0N/A
0N/A/**
0N/A * Deletes the row in the underlying data source that corresponds to
0N/A * a row that has been deleted in the given <code> CachedRowSet</code> object
0N/A * and returns <code>false</code> if the deletion was successful.
0N/A * <P>
0N/A * This method is called internally by this writer's <code>writeData</code>
0N/A * method when a row in the rowset has been deleted. The values in the
0N/A * deleted row are the same as those that are stored in the original row
0N/A * of the given <code>CachedRowSet</code> object. If the values in the
0N/A * original row differ from the row in the underlying data source, the row
0N/A * in the data source is not deleted, and <code>deleteOriginalRow</code>
0N/A * returns <code>true</code> to indicate that there was a conflict.
0N/A *
0N/A *
0N/A * @return <code>false</code> if the deletion was successful, which means that
0N/A * there was no conflict; <code>true</code> otherwise
0N/A * @throws SQLException if there was a database access error
0N/A */
0N/A private boolean deleteOriginalRow(CachedRowSet crs, CachedRowSetImpl crsRes) throws SQLException {
0N/A PreparedStatement pstmt;
0N/A int i;
0N/A int idx = 0;
0N/A String strSelect;
0N/A // Select the row from the database.
0N/A ResultSet origVals = crs.getOriginalRow();
0N/A origVals.next();
0N/A
0N/A deleteWhere = buildWhereClause(deleteWhere, origVals);
0N/A pstmt = con.prepareStatement(selectCmd + deleteWhere,
0N/A ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
0N/A
0N/A for (i = 0; i < keyCols.length; i++) {
0N/A if (params[i] != null) {
0N/A pstmt.setObject(++idx, params[i]);
0N/A } else {
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A try {
0N/A pstmt.setMaxRows(crs.getMaxRows());
0N/A pstmt.setMaxFieldSize(crs.getMaxFieldSize());
0N/A pstmt.setEscapeProcessing(crs.getEscapeProcessing());
0N/A pstmt.setQueryTimeout(crs.getQueryTimeout());
0N/A } catch (Exception ex) {
0N/A /*
0N/A * Older driver don't support these operations...
0N/A */
0N/A ;
0N/A }
0N/A
0N/A ResultSet rs = pstmt.executeQuery();
0N/A
0N/A if (rs.next() == true) {
0N/A if (rs.next()) {
0N/A // more than one row
0N/A return true;
0N/A }
0N/A rs.first();
0N/A
0N/A // Now check all the values in rs to be same in
0N/A // db also before actually going ahead with deleting
0N/A boolean boolChanged = false;
0N/A
0N/A crsRes.moveToInsertRow();
0N/A
0N/A for (i = 1; i <= crs.getMetaData().getColumnCount(); i++) {
0N/A
0N/A Object original = origVals.getObject(i);
0N/A Object changed = rs.getObject(i);
0N/A
0N/A if(original != null && changed != null ) {
0N/A if(! (original.toString()).equals(changed.toString()) ) {
0N/A boolChanged = true;
0N/A crsRes.updateObject(i,origVals.getObject(i));
0N/A }
0N/A } else {
0N/A crsRes.updateNull(i);
0N/A }
0N/A }
0N/A
0N/A crsRes.insertRow();
0N/A crsRes.moveToCurrentRow();
0N/A
0N/A if(boolChanged) {
0N/A // do not delete as values in db have changed
0N/A // deletion will not happen for this row from db
0N/A // exit now returning true. i.e. conflict
0N/A return true;
0N/A } else {
0N/A // delete the row.
0N/A // Go ahead with deleting,
0N/A // don't do anything here
0N/A }
0N/A
0N/A String cmd = deleteCmd + deleteWhere;
0N/A pstmt = con.prepareStatement(cmd);
0N/A
0N/A idx = 0;
0N/A for (i = 0; i < keyCols.length; i++) {
0N/A if (params[i] != null) {
0N/A pstmt.setObject(++idx, params[i]);
0N/A } else {
0N/A continue;
0N/A }
0N/A }
0N/A
0N/A if (pstmt.executeUpdate() != 1) {
0N/A return true;
0N/A }
0N/A pstmt.close();
0N/A } else {
0N/A // didn't find the row
0N/A return true;
0N/A }
0N/A
0N/A // no conflict
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Sets the reader for this writer to the given reader.
0N/A *
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A public void setReader(CachedRowSetReader reader) throws SQLException {
0N/A this.reader = reader;
0N/A }
0N/A
0N/A /**
0N/A * Gets the reader for this writer.
0N/A *
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A public CachedRowSetReader getReader() throws SQLException {
0N/A return reader;
0N/A }
0N/A
0N/A /**
0N/A * Composes a <code>SELECT</code>, <code>UPDATE</code>, <code>INSERT</code>,
0N/A * and <code>DELETE</code> statement that can be used by this writer to
0N/A * write data to the data source backing the given <code>CachedRowSet</code>
0N/A * object.
0N/A *
0N/A * @ param caller a <code>CachedRowSet</code> object for which this
0N/A * <code>CachedRowSetWriter</code> object is the writer
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private void initSQLStatements(CachedRowSet caller) throws SQLException {
0N/A
0N/A int i;
0N/A
0N/A callerMd = caller.getMetaData();
0N/A callerColumnCount = callerMd.getColumnCount();
0N/A if (callerColumnCount < 1)
0N/A // No data, so return.
0N/A return;
0N/A
0N/A /*
0N/A * If the RowSet has a Table name we should use it.
0N/A * This is really a hack to get round the fact that
0N/A * a lot of the jdbc drivers can't provide the tab.
0N/A */
0N/A String table = caller.getTableName();
0N/A if (table == null) {
0N/A /*
0N/A * attempt to build a table name using the info
0N/A * that the driver gave us for the first column
0N/A * in the source result set.
0N/A */
0N/A table = callerMd.getTableName(1);
0N/A if (table == null || table.length() == 0) {
0N/A throw new SQLException(resBundle.handleGetObject("crswriter.tname").toString());
0N/A }
0N/A }
0N/A String catalog = callerMd.getCatalogName(1);
0N/A String schema = callerMd.getSchemaName(1);
0N/A DatabaseMetaData dbmd = con.getMetaData();
0N/A
0N/A /*
0N/A * Compose a SELECT statement. There are three parts.
0N/A */
0N/A
0N/A // Project List
0N/A selectCmd = "SELECT ";
0N/A for (i=1; i <= callerColumnCount; i++) {
0N/A selectCmd += callerMd.getColumnName(i);
0N/A if ( i < callerMd.getColumnCount() )
0N/A selectCmd += ", ";
0N/A else
0N/A selectCmd += " ";
0N/A }
0N/A
0N/A // FROM clause.
0N/A selectCmd += "FROM " + buildTableName(dbmd, catalog, schema, table);
0N/A
0N/A /*
0N/A * Compose an UPDATE statement.
0N/A */
0N/A updateCmd = "UPDATE " + buildTableName(dbmd, catalog, schema, table);
0N/A
0N/A
0N/A /**
0N/A * The following block of code is for checking a particular type of
0N/A * query where in there is a where clause. Without this block, if a
0N/A * SQL statement is built the "where" clause will appear twice hence
0N/A * the DB errors out and a SQLException is thrown. This code also
0N/A * considers that the where clause is in the right place as the
0N/A * CachedRowSet object would already have been populated with this
0N/A * query before coming to this point.
0N/A **/
0N/A
0N/A String tempupdCmd = updateCmd.toLowerCase();
0N/A
0N/A int idxupWhere = tempupdCmd.indexOf("where");
0N/A
0N/A if(idxupWhere != -1)
0N/A {
0N/A updateCmd = updateCmd.substring(0,idxupWhere);
0N/A }
0N/A updateCmd += "SET ";
0N/A
0N/A /*
0N/A * Compose an INSERT statement.
0N/A */
0N/A insertCmd = "INSERT INTO " + buildTableName(dbmd, catalog, schema, table);
0N/A // Column list
0N/A insertCmd += "(";
0N/A for (i=1; i <= callerColumnCount; i++) {
0N/A insertCmd += callerMd.getColumnName(i);
0N/A if ( i < callerMd.getColumnCount() )
0N/A insertCmd += ", ";
0N/A else
0N/A insertCmd += ") VALUES (";
0N/A }
0N/A for (i=1; i <= callerColumnCount; i++) {
0N/A insertCmd += "?";
0N/A if (i < callerColumnCount)
0N/A insertCmd += ", ";
0N/A else
0N/A insertCmd += ")";
0N/A }
0N/A
0N/A /*
0N/A * Compose a DELETE statement.
0N/A */
0N/A deleteCmd = "DELETE FROM " + buildTableName(dbmd, catalog, schema, table);
0N/A
0N/A /*
0N/A * set the key desriptors that will be
0N/A * needed to construct where clauses.
0N/A */
0N/A buildKeyDesc(caller);
0N/A }
0N/A
0N/A /**
0N/A * Returns a fully qualified table name built from the given catalog and
0N/A * table names. The given metadata object is used to get the proper order
0N/A * and separator.
0N/A *
0N/A * @param dbmd a <code>DatabaseMetaData</code> object that contains metadata
0N/A * about this writer's <code>CachedRowSet</code> object
0N/A * @param catalog a <code>String</code> object with the rowset's catalog
0N/A * name
0N/A * @param table a <code>String</code> object with the name of the table from
0N/A * which this writer's rowset was derived
0N/A * @return a <code>String</code> object with the fully qualified name of the
0N/A * table from which this writer's rowset was derived
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private String buildTableName(DatabaseMetaData dbmd,
0N/A String catalog, String schema, String table) throws SQLException {
0N/A
0N/A // trim all the leading and trailing whitespaces,
0N/A // white spaces can never be catalog, schema or a table name.
0N/A
2823N/A String cmd = "";
0N/A
0N/A catalog = catalog.trim();
0N/A schema = schema.trim();
0N/A table = table.trim();
0N/A
0N/A if (dbmd.isCatalogAtStart() == true) {
0N/A if (catalog != null && catalog.length() > 0) {
0N/A cmd += catalog + dbmd.getCatalogSeparator();
0N/A }
0N/A if (schema != null && schema.length() > 0) {
0N/A cmd += schema + ".";
0N/A }
0N/A cmd += table;
0N/A } else {
0N/A if (schema != null && schema.length() > 0) {
0N/A cmd += schema + ".";
0N/A }
0N/A cmd += table;
0N/A if (catalog != null && catalog.length() > 0) {
0N/A cmd += dbmd.getCatalogSeparator() + catalog;
0N/A }
0N/A }
0N/A cmd += " ";
0N/A return cmd;
0N/A }
0N/A
0N/A /**
0N/A * Assigns to the given <code>CachedRowSet</code> object's
0N/A * <code>params</code>
0N/A * field an array whose length equals the number of columns needed
0N/A * to uniquely identify a row in the rowset. The array is given
0N/A * values by the method <code>buildWhereClause</code>.
0N/A * <P>
0N/A * If the <code>CachedRowSet</code> object's <code>keyCols</code>
0N/A * field has length <code>0</code> or is <code>null</code>, the array
0N/A * is set with the column number of every column in the rowset.
0N/A * Otherwise, the array in the field <code>keyCols</code> is set with only
0N/A * the column numbers of the columns that are required to form a unique
0N/A * identifier for a row.
0N/A *
0N/A * @param crs the <code>CachedRowSet</code> object for which this
0N/A * <code>CachedRowSetWriter</code> object is the writer
0N/A *
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private void buildKeyDesc(CachedRowSet crs) throws SQLException {
0N/A
0N/A keyCols = crs.getKeyColumns();
2741N/A ResultSetMetaData resultsetmd = crs.getMetaData();
0N/A if (keyCols == null || keyCols.length == 0) {
2741N/A ArrayList<Integer> listKeys = new ArrayList<Integer>();
2741N/A
2741N/A for (int i = 0; i < callerColumnCount; i++ ) {
2741N/A if(resultsetmd.getColumnType(i+1) != java.sql.Types.CLOB &&
2741N/A resultsetmd.getColumnType(i+1) != java.sql.Types.STRUCT &&
2741N/A resultsetmd.getColumnType(i+1) != java.sql.Types.SQLXML &&
2741N/A resultsetmd.getColumnType(i+1) != java.sql.Types.BLOB &&
2741N/A resultsetmd.getColumnType(i+1) != java.sql.Types.ARRAY &&
2741N/A resultsetmd.getColumnType(i+1) != java.sql.Types.OTHER )
2741N/A listKeys.add(i+1);
0N/A }
2741N/A keyCols = new int[listKeys.size()];
2741N/A for (int i = 0; i < listKeys.size(); i++ )
2741N/A keyCols[i] = listKeys.get(i);
0N/A }
0N/A params = new Object[keyCols.length];
0N/A }
0N/A
0N/A /**
0N/A * Constructs an SQL <code>WHERE</code> clause using the given
0N/A * string as a starting point. The resulting clause will contain
0N/A * a column name and " = ?" for each key column, that is, each column
0N/A * that is needed to form a unique identifier for a row in the rowset.
0N/A * This <code>WHERE</code> clause can be added to
0N/A * a <code>PreparedStatement</code> object that updates, inserts, or
0N/A * deletes a row.
0N/A * <P>
0N/A * This method uses the given result set to access values in the
0N/A * <code>CachedRowSet</code> object that called this writer. These
0N/A * values are used to build the array of parameters that will serve as
0N/A * replacements for the "?" parameter placeholders in the
0N/A * <code>PreparedStatement</code> object that is sent to the
0N/A * <code>CachedRowSet</code> object's underlying data source.
0N/A *
0N/A * @param whereClause a <code>String</code> object that is an empty
0N/A * string ("")
0N/A * @param rs a <code>ResultSet</code> object that can be used
0N/A * to access the <code>CachedRowSet</code> object's data
0N/A * @return a <code>WHERE</code> clause of the form "<code>WHERE</code>
0N/A * columnName = ? AND columnName = ? AND columnName = ? ..."
0N/A * @throws SQLException if a database access error occurs
0N/A */
0N/A private String buildWhereClause(String whereClause,
0N/A ResultSet rs) throws SQLException {
0N/A whereClause = "WHERE ";
0N/A
0N/A for (int i = 0; i < keyCols.length; i++) {
0N/A if (i > 0) {
0N/A whereClause += "AND ";
0N/A }
0N/A whereClause += callerMd.getColumnName(keyCols[i]);
0N/A params[i] = rs.getObject(keyCols[i]);
0N/A if (rs.wasNull() == true) {
0N/A whereClause += " IS NULL ";
0N/A } else {
0N/A whereClause += " = ? ";
0N/A }
0N/A }
0N/A return whereClause;
0N/A }
0N/A
0N/A void updateResolvedConflictToDB(CachedRowSet crs, Connection con) throws SQLException {
0N/A //String updateExe = ;
0N/A PreparedStatement pStmt ;
0N/A String strWhere = "WHERE " ;
0N/A String strExec =" ";
0N/A String strUpdate = "UPDATE ";
0N/A int icolCount = crs.getMetaData().getColumnCount();
0N/A int keyColumns[] = crs.getKeyColumns();
0N/A Object param[];
0N/A String strSet="";
0N/A
0N/A strWhere = buildWhereClause(strWhere, crs);
0N/A
0N/A if (keyColumns == null || keyColumns.length == 0) {
0N/A keyColumns = new int[icolCount];
0N/A for (int i = 0; i < keyColumns.length; ) {
0N/A keyColumns[i] = ++i;
0N/A }
0N/A }
0N/A param = new Object[keyColumns.length];
0N/A
0N/A strUpdate = "UPDATE " + buildTableName(con.getMetaData(),
0N/A crs.getMetaData().getCatalogName(1),
0N/A crs.getMetaData().getSchemaName(1),
0N/A crs.getTableName());
0N/A
0N/A // changed or updated values will become part of
0N/A // set clause here
0N/A strUpdate += "SET ";
0N/A
0N/A boolean first = true;
0N/A
0N/A for (int i=1; i<=icolCount;i++) {
0N/A if (crs.columnUpdated(i)) {
0N/A if (first == false) {
0N/A strSet += ", ";
0N/A }
0N/A strSet += crs.getMetaData().getColumnName(i);
0N/A strSet += " = ? ";
0N/A first = false;
0N/A } //end if
0N/A } //end for
0N/A
0N/A // keycols will become part of where clause
0N/A strUpdate += strSet;
0N/A strWhere = "WHERE ";
0N/A
0N/A for (int i = 0; i < keyColumns.length; i++) {
0N/A if (i > 0) {
0N/A strWhere += "AND ";
0N/A }
0N/A strWhere += crs.getMetaData().getColumnName(keyColumns[i]);
0N/A param[i] = crs.getObject(keyColumns[i]);
0N/A if (crs.wasNull() == true) {
0N/A strWhere += " IS NULL ";
0N/A } else {
0N/A strWhere += " = ? ";
0N/A }
0N/A }
0N/A strUpdate += strWhere;
0N/A
0N/A pStmt = con.prepareStatement(strUpdate);
0N/A
0N/A int idx =0;
0N/A for (int i = 0; i < icolCount; i++) {
0N/A if(crs.columnUpdated(i+1)) {
0N/A Object obj = crs.getObject(i+1);
0N/A if (obj != null) {
0N/A pStmt.setObject(++idx, obj);
0N/A } else {
0N/A pStmt.setNull(i + 1,crs.getMetaData().getColumnType(i + 1));
0N/A } //end if ..else
0N/A } //end if crs.column...
0N/A } //end for
0N/A
0N/A // Set the key cols for after WHERE =? clause
0N/A for (int i = 0; i < keyColumns.length; i++) {
0N/A if (param[i] != null) {
0N/A pStmt.setObject(++idx, param[i]);
0N/A }
0N/A }
0N/A
0N/A int id = pStmt.executeUpdate();
0N/A }
0N/A
0N/A
0N/A /**
0N/A *
0N/A */
0N/A public void commit() throws SQLException {
0N/A con.commit();
0N/A if (reader.getCloseConnection() == true) {
0N/A con.close();
0N/A }
0N/A }
0N/A
0N/A public void commit(CachedRowSetImpl crs, boolean updateRowset) throws SQLException {
0N/A con.commit();
0N/A if(updateRowset) {
0N/A if(crs.getCommand() != null)
0N/A crs.execute(con);
0N/A }
0N/A
0N/A if (reader.getCloseConnection() == true) {
0N/A con.close();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A *
0N/A */
0N/A public void rollback() throws SQLException {
0N/A con.rollback();
0N/A if (reader.getCloseConnection() == true) {
0N/A con.close();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A *
0N/A */
0N/A public void rollback(Savepoint s) throws SQLException {
0N/A con.rollback(s);
0N/A if (reader.getCloseConnection() == true) {
0N/A con.close();
0N/A }
0N/A }
0N/A
2741N/A private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
2741N/A // Default state initialization happens here
2741N/A ois.defaultReadObject();
2741N/A // Initialization of Res Bundle happens here .
2741N/A try {
2741N/A resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
2741N/A } catch(IOException ioe) {
2741N/A throw new RuntimeException(ioe);
2741N/A }
2741N/A
2741N/A }
2741N/A
2741N/A static final long serialVersionUID =-8506030970299413976L;
0N/A}