001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.harmony.sql.internal.rowset;
018:
019: import java.sql.Connection;
020: import java.sql.PreparedStatement;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023:
024: import javax.sql.RowSetInternal;
025: import javax.sql.RowSetWriter;
026: import javax.sql.rowset.CachedRowSet;
027: import javax.sql.rowset.spi.SyncProviderException;
028:
029: public class CachedRowSetWriter implements RowSetWriter {
030:
031: private CachedRowSet originalRowSet;
032:
033: private CachedRowSetImpl currentRowSet;
034:
035: private Connection originalConnection;
036:
037: private String tableName;
038:
039: private String[] colNames;
040:
041: private int columnCount;
042:
043: public void setConnection(Connection conn) {
044: originalConnection = conn;
045: }
046:
047: public Connection getConnection() {
048: return originalConnection;
049: }
050:
051: /**
052: * TODO add transaction
053: */
054: public boolean writeData(RowSetInternal theRowSet)
055: throws SQLException {
056: initial(theRowSet);
057: // analyse every row and do responsible task.
058: currentRowSet.beforeFirst();// currentRowSet.first();
059: originalRowSet.beforeFirst();// originalRowSet.first();
060: while (currentRowSet.next()) {
061: if (currentRowSet.rowInserted()) {
062: insertCurrentRow();
063: } else if (currentRowSet.rowDeleted()) {
064: if (isConflictExistForCurrentRow()) {
065: // TODO: conflict exists, should throw SyncProviderException
066: throw new SyncProviderException();
067: }
068:
069: deleteCurrentRow();
070:
071: } else if (currentRowSet.rowUpdated()) {
072: if (isConflictExistForCurrentRow()) {
073: // TODO: conflict exists, should throw SyncProviderException
074: throw new SyncProviderException();
075: }
076:
077: updateCurrentRow();
078: }
079: }
080: // TODO release resource
081: return true;
082: }
083:
084: /**
085: * Insert the RowSet's current row to DB
086: *
087: * @throws SQLException
088: */
089: @SuppressWarnings("nls")
090: private void insertCurrentRow() throws SQLException {
091: /*
092: * the first step: generate the insert SQL
093: */
094: StringBuffer insertSQL = new StringBuffer("INSERT INTO "
095: + tableName + "(");
096: StringBuffer insertColNames = new StringBuffer();
097: StringBuffer insertPlaceholder = new StringBuffer();
098: Object[] insertColValues = new Object[columnCount];
099:
100: int updateCount = 0;
101: for (int i = 1; i <= columnCount; i++) {
102: boolean isColUpdate = currentRowSet.columnUpdated(i);
103: if (isColUpdate) {
104: insertColNames.append(colNames[i - 1] + ",");
105: insertPlaceholder.append("?,");
106: insertColValues[updateCount] = currentRowSet
107: .getObject(i);
108: updateCount++;
109: }
110: }
111: if (updateCount == 0) {
112: return;
113: }
114:
115: insertSQL.append(subStringN(insertColNames.toString(), 1));
116: insertSQL.append(") values (");
117: insertSQL.append(subStringN(insertPlaceholder.toString(), 1));
118: insertSQL.append(")");
119:
120: /*
121: * the second step: execute SQL
122: */
123: PreparedStatement preSt = getConnection().prepareStatement(
124: insertSQL.toString());
125: for (int i = 0; i < updateCount; i++) {
126: preSt.setObject(i + 1, insertColValues[i]);
127: }
128: try {
129: preSt.executeUpdate();
130: } catch (SQLException e) {
131: // TODO generate SyncProviderException
132: throw new SyncProviderException();
133: } finally {
134: preSt.close();
135: }
136: }
137:
138: /**
139: * Delete the current row from DB
140: *
141: * @throws SQLException
142: */
143: private void deleteCurrentRow() throws SQLException {
144: /*
145: * the first step: generate the delete SQL
146: */
147: StringBuffer deleteSQL = new StringBuffer(
148: "DELETE FROM " + tableName //$NON-NLS-1$
149: + " WHERE "); //$NON-NLS-1$
150: deleteSQL.append(generateQueryCondition());
151:
152: /*
153: * the second step: execute SQL
154: */
155: PreparedStatement preSt = getConnection().prepareStatement(
156: deleteSQL.toString());
157: fillParamInPreStatement(preSt, 1);
158: preSt.executeUpdate();
159: }
160:
161: /**
162: * Update the current row to DB
163: *
164: * @throws SQLException
165: */
166: @SuppressWarnings("nls")
167: private void updateCurrentRow() throws SQLException {
168: /*
169: * the first step: generate the delete SQL
170: */
171: StringBuffer updateSQL = new StringBuffer("UPDATE " + tableName
172: + " SET ");
173: StringBuffer updateCols = new StringBuffer();
174: Object[] updateColValues = new Object[columnCount];
175: int[] updateColIndexs = new int[columnCount];
176:
177: int updateCount = 0;
178: for (int i = 1; i <= columnCount; i++) {
179: boolean isColUpdate = currentRowSet.columnUpdated(i);
180: if (isColUpdate) {
181: updateCols.append(colNames[i - 1] + " = ?, ");
182: updateColValues[updateCount] = currentRowSet
183: .getObject(i);
184: updateColIndexs[updateCount] = i;
185: updateCount++;
186: }
187: }
188: if (updateCount == 0) {
189: return;
190: }
191: updateSQL.append(subStringN(updateCols.toString(), 2));
192: updateSQL.append(" WHERE ");
193: updateSQL.append(generateQueryCondition());
194:
195: /*
196: * the second step: execute SQL
197: */
198: PreparedStatement preSt = getConnection().prepareStatement(
199: updateSQL.toString());
200: // the SET part of SQL
201: for (int i = 0; i < updateCount; i++) {
202: if (updateColValues[i] == null) {
203: preSt.setNull(i + 1, currentRowSet.getMetaData()
204: .getColumnType(updateColIndexs[i]));
205: } else {
206: preSt.setObject(i + 1, updateColValues[i]);
207: }
208: }
209: // the WHERE part of SQL
210: fillParamInPreStatement(preSt, updateCount + 1);
211: try {
212: preSt.executeUpdate();
213: } catch (SQLException e) {
214: // TODO generate SyncProviderException
215: throw new SyncProviderException();
216: } finally {
217: preSt.close();
218: }
219: }
220:
221: private void initial(RowSetInternal theRowSet) throws SQLException {
222: currentRowSet = (CachedRowSetImpl) theRowSet;
223: // initial environment
224: originalRowSet = (CachedRowSet) currentRowSet.getOriginal();
225: // originalConnection = currentRowSet.getConnection();
226: tableName = currentRowSet.getTableName();
227: columnCount = currentRowSet.getMetaData().getColumnCount();
228: colNames = new String[columnCount];
229: for (int i = 1; i <= columnCount; i++) {
230: colNames[i - 1] = currentRowSet.getMetaData()
231: .getColumnName(i);
232: }
233: }
234:
235: /**
236: * Compare the current row's data between database and CachedRowSet to check
237: * whether it has been changed in database.
238: *
239: * @return if conflict exists, return true; else, return false
240: * @throws SQLException
241: */
242: private boolean isConflictExistForCurrentRow() throws SQLException {
243: boolean isExist = true;
244: originalRowSet.absolute(currentRowSet.getRow()); // the original data
245:
246: StringBuffer querySQL = new StringBuffer(
247: "SELECT COUNT(*) FROM " //$NON-NLS-1$
248: + tableName + " WHERE "); //$NON-NLS-1$
249: querySQL.append(generateQueryCondition());
250:
251: PreparedStatement preSt = getConnection().prepareStatement(
252: querySQL.toString());
253: fillParamInPreStatement(preSt, 1);
254: ResultSet queryRs = preSt.executeQuery();
255: if (queryRs.next()) {
256: if (queryRs.getInt(1) == 1) {
257: isExist = false;
258: }
259: }
260: queryRs.close();
261: preSt.close();
262:
263: return isExist;
264: }
265:
266: /**
267: * Generate the query condition after the keyword "WHERE" in SQL. Expression
268: * likes as: COLUMN1 = ? AND COLUMN2 = ?
269: *
270: * @return the SQL query expression
271: */
272: @SuppressWarnings("nls")
273: private String generateQueryCondition() throws SQLException {
274: StringBuffer queryCondtion = new StringBuffer(" ");
275: for (int i = 0; i < colNames.length; i++) {
276: if (originalRowSet.getObject(i + 1) == null) {
277: queryCondtion.append(colNames[i] + " is null ");
278: } else {
279: queryCondtion.append(colNames[i] + " = ? ");
280: }
281: if (i != colNames.length - 1) {
282: queryCondtion.append(" and ");
283: }
284: }
285: return queryCondtion.toString();
286: }
287:
288: /**
289: * Fill all the parameters in PreparedStatement
290: *
291: * @param preSt
292: * PreparedStatement
293: * @param fromIndex
294: * It must be greater than 0
295: * @throws SQLException
296: */
297: private void fillParamInPreStatement(PreparedStatement preSt,
298: int fromIndex) throws SQLException {
299: int notNullCount = fromIndex;
300: for (int i = 1; i <= columnCount; i++) {
301: if (originalRowSet.getObject(i) != null) {
302: preSt.setObject(notNullCount, originalRowSet
303: .getObject(i));
304: notNullCount++;
305: }
306: }
307: }
308:
309: private String subStringN(String input, int n) {
310: return input.substring(0, input.length() - n);
311: }
312: }
|