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