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 * 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 * 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 * 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. 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 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 * 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 * 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 * @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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * The number of columns in the <code>CachedRowSet</code> object 0N/A * for which this <code>CachedRowSetWriter</code> object is the writer. 0N/A * This <code>CachedRowSet<code> will hold the conflicting values 0N/A * retrieved from the db and hold it. 0N/A * This <code>ArrayList<code> will hold the values of SyncResolver.* 0N/A * This will check whether the same field value has changed both 0N/A * in database and CachedRowSet. 0N/A * This will hold the number of cols for which the values have 0N/A * changed only in database. 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 * 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 * <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 * <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 * <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 * <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 * <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 * <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 * @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 // We assume caller is a CachedRowSet 0N/A // crsResolve = new CachedRowSetImpl(); 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 // Don't change the connection or transaction properties. This will fail in a 0N/A if (con.getAutoCommit() == true) { 0N/A con.setAutoCommit(false); 0N/A con.setTransactionIsolation(crs.getTransactionIsolation()); 0N/A // moved outside the insert inner loop 0N/A //pstmtIns = con.prepareStatement(insertCmd); 0N/A // No data, so return success. 0N/A // We need to see rows marked for deletion. 0N/A // Look at all the rows. 0N/A // The row has been deleted. 0N/A // delete happened without any occurrence of conflicts 0N/A // so update status accordingly 0N/A // The row has been inserted. 0N/A // insert happened without any occurrence of conflicts 0N/A // so update status accordingly 0N/A // The row has been updated. 0N/A // update happened without any occurrence of conflicts 0N/A // so update status accordingly 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 // close the insert statement 0N/A // ignore status for index = 0 which is set to null 0N/A // there is at least one conflict which needs to be resolved 0N/A //SyncResolver syncRes = spe.getSyncResolver(); 0N/A if (conflict == true) { 0N/A if (reader.getCloseConnection() == true) { 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 * 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 * @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 // Select the row from the database. 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 // Older driver don't support these operations. 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 // don't close the rs 0N/A // we require the record in rs to be used. 0N/A // how many fields need to be updated 0N/A // There's only one row and the cursor 0N/A // needs to be on that row. 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 // look up the class in the map 2741N/A // create new instance of the class 2741N/A // get the attributes from the struct 2741N/A // create the SQLInput "stream" 0N/A // reset boolNull if it had been set 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 // 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 // Set the boolNull to false, 0N/A // in order to set the actual value; 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 // 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 // Set the boolNull to false, 0N/A // in order to set the actual value; 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 /** 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 //nothing to update in this case since values are equal 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 // 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 // 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 // Synchronization happens only when data when fetching is 0N/A // same or at most has changed in cachedrowset 0N/A // check orig value with what is there in crs for a column 0N/A // before updation in crs. 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 // Here the value has changed in the db after 0N/A // Plus store this row from CachedRowSet and keep it 0N/A // in a new CachedRowSet 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 // Comments needed here 0N/A // Comments needed here 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 * 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 * 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 * 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 // if executeUpdate fails it will come here, 0N/A // update crsResolve with null rows 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 * @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 * 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 * 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 * 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 * @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 // Select the row from the database. 0N/A * Older driver don't support these operations... 0N/A // more than one row 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 // 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 // Go ahead with deleting, 0N/A // don't do anything here 0N/A // didn't find the row 0N/A * Sets the reader for this writer to the given reader. 0N/A * @throws SQLException if a database access error occurs 0N/A * Gets the reader for this writer. 0N/A * @throws SQLException if a database access error occurs 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 * @ 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 // No data, so return. 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 * 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 * Compose a SELECT statement. There are three parts. 0N/A * Compose an UPDATE statement. 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 * Compose an INSERT statement. 0N/A * Compose a DELETE statement. 0N/A * set the key desriptors that will be 0N/A * needed to construct where clauses. 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 * @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 * @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 // trim all the leading and trailing whitespaces, 0N/A // white spaces can never be catalog, schema or a table name. 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 * 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 * @param crs the <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 * 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 * 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 * @param whereClause a <code>String</code> object that is an empty 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 //String updateExe = ; 0N/A // changed or updated values will become part of 0N/A // keycols will become part of where clause 0N/A }
//end if crs.column... 0N/A // Set the key cols for after WHERE =? clause 2741N/A // Default state initialization happens here 2741N/A // Initialization of Res Bundle happens here .