KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > jdbc > BatchAction


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access.jdbc;
21
22 import java.sql.Connection JavaDoc;
23 import java.sql.PreparedStatement JavaDoc;
24 import java.sql.ResultSet JavaDoc;
25 import java.sql.SQLException JavaDoc;
26 import java.sql.Statement JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import org.apache.cayenne.CayenneException;
33 import org.apache.cayenne.access.OperationObserver;
34 import org.apache.cayenne.access.OptimisticLockException;
35 import org.apache.cayenne.access.QueryLogger;
36 import org.apache.cayenne.access.ResultIterator;
37 import org.apache.cayenne.access.trans.BatchQueryBuilder;
38 import org.apache.cayenne.access.trans.DeleteBatchQueryBuilder;
39 import org.apache.cayenne.access.trans.InsertBatchQueryBuilder;
40 import org.apache.cayenne.access.trans.UpdateBatchQueryBuilder;
41 import org.apache.cayenne.dba.DbAdapter;
42 import org.apache.cayenne.dba.TypesMapping;
43 import org.apache.cayenne.map.DbAttribute;
44 import org.apache.cayenne.map.EntityResolver;
45 import org.apache.cayenne.query.BatchQuery;
46 import org.apache.cayenne.query.DeleteBatchQuery;
47 import org.apache.cayenne.query.InsertBatchQuery;
48 import org.apache.cayenne.query.UpdateBatchQuery;
49
50 /**
51  * @since 1.2
52  * @author Andrus Adamchik
53  */

54 public class BatchAction extends BaseSQLAction {
55
56     protected boolean batch;
57     protected BatchQuery query;
58     protected RowDescriptor keyRowDescriptor;
59
60     public BatchAction(BatchQuery batchQuery, DbAdapter adapter,
61             EntityResolver entityResolver) {
62         super(adapter, entityResolver);
63         this.query = batchQuery;
64     }
65
66     public boolean isBatch() {
67         return batch;
68     }
69
70     public void setBatch(boolean runningAsBatch) {
71         this.batch = runningAsBatch;
72     }
73
74     public void performAction(Connection JavaDoc connection, OperationObserver observer)
75             throws SQLException JavaDoc, Exception JavaDoc {
76
77         BatchQueryBuilder queryBuilder = createBuilder();
78         boolean generatesKeys = hasGeneratedKeys();
79
80         if (batch && !generatesKeys) {
81             runAsBatch(connection, queryBuilder, observer);
82         }
83         else {
84             runAsIndividualQueries(connection, queryBuilder, observer, generatesKeys);
85         }
86     }
87
88     protected BatchQueryBuilder createBuilder() throws CayenneException {
89         if (query instanceof InsertBatchQuery) {
90             return new InsertBatchQueryBuilder(getAdapter());
91         }
92         else if (query instanceof UpdateBatchQuery) {
93             return new UpdateBatchQueryBuilder(getAdapter());
94         }
95         else if (query instanceof DeleteBatchQuery) {
96             return new DeleteBatchQueryBuilder(getAdapter());
97         }
98         else {
99             throw new CayenneException("Unsupported batch query: " + query);
100         }
101     }
102
103     protected void runAsBatch(
104             Connection JavaDoc con,
105             BatchQueryBuilder queryBuilder,
106             OperationObserver delegate) throws SQLException JavaDoc, Exception JavaDoc {
107
108         String JavaDoc queryStr = queryBuilder.createSqlString(query);
109         boolean isLoggable = QueryLogger.isLoggable();
110
111         // log batch SQL execution
112
QueryLogger.logQuery(queryStr, Collections.EMPTY_LIST);
113
114         // run batch
115
query.reset();
116
117         PreparedStatement JavaDoc statement = con.prepareStatement(queryStr);
118         try {
119             while (query.next()) {
120
121                 if (isLoggable) {
122                     QueryLogger.logQueryParameters("batch bind", queryBuilder
123                             .getParameterValues(query));
124                 }
125
126                 queryBuilder.bindParameters(statement, query);
127                 statement.addBatch();
128             }
129
130             // execute the whole batch
131
int[] results = statement.executeBatch();
132             delegate.nextBatchCount(query, results);
133
134             if (isLoggable) {
135                 int totalUpdateCount = 0;
136                 for (int i = 0; i < results.length; i++) {
137
138                     // this means Statement.SUCCESS_NO_INFO or Statement.EXECUTE_FAILED
139
if (results[i] < 0) {
140                         totalUpdateCount = Statement.SUCCESS_NO_INFO;
141                         break;
142                     }
143
144                     totalUpdateCount += results[i];
145                 }
146
147                 QueryLogger.logUpdateCount(totalUpdateCount);
148             }
149         }
150         finally {
151             try {
152                 statement.close();
153             }
154             catch (Exception JavaDoc e) {
155             }
156         }
157     }
158
159     /**
160      * Executes batch as individual queries over the same prepared statement.
161      */

162     protected void runAsIndividualQueries(
163             Connection JavaDoc connection,
164             BatchQueryBuilder queryBuilder,
165             OperationObserver delegate,
166             boolean generatesKeys) throws SQLException JavaDoc, Exception JavaDoc {
167
168         boolean isLoggable = QueryLogger.isLoggable();
169         boolean useOptimisticLock = query.isUsingOptimisticLocking();
170
171         String JavaDoc queryStr = queryBuilder.createSqlString(query);
172
173         // log batch SQL execution
174
QueryLogger.logQuery(queryStr, Collections.EMPTY_LIST);
175
176         // run batch queries one by one
177
query.reset();
178
179         PreparedStatement JavaDoc statement = (generatesKeys) ? connection.prepareStatement(
180                 queryStr,
181                 Statement.RETURN_GENERATED_KEYS) : connection.prepareStatement(queryStr);
182         try {
183             while (query.next()) {
184                 if (isLoggable) {
185                     QueryLogger.logQueryParameters("bind", queryBuilder
186                             .getParameterValues(query));
187                 }
188
189                 queryBuilder.bindParameters(statement, query);
190
191                 int updated = statement.executeUpdate();
192                 if (useOptimisticLock && updated != 1) {
193
194                     Map JavaDoc snapshot = Collections.EMPTY_MAP;
195                     if (query instanceof UpdateBatchQuery) {
196                         snapshot = ((UpdateBatchQuery) query).getCurrentQualifier();
197                     }
198                     else if (query instanceof DeleteBatchQuery) {
199                         snapshot = ((DeleteBatchQuery) query).getCurrentQualifier();
200                     }
201
202                     throw new OptimisticLockException(
203                             query.getDbEntity(),
204                             queryStr,
205                             snapshot);
206                 }
207
208                 delegate.nextCount(query, updated);
209
210                 if (generatesKeys) {
211                     processGeneratedKeys(statement, delegate);
212                 }
213
214                 if (isLoggable) {
215                     QueryLogger.logUpdateCount(updated);
216                 }
217             }
218         }
219         finally {
220             try {
221                 statement.close();
222             }
223             catch (Exception JavaDoc e) {
224             }
225         }
226     }
227
228     /**
229      * Returns whether BatchQuery generates any keys.
230      */

231     protected boolean hasGeneratedKeys() {
232         // see if we are configured to support generated keys
233
if (!adapter.supportsGeneratedKeys()) {
234             return false;
235         }
236
237         // see if the query needs them
238
if (query instanceof InsertBatchQuery) {
239
240             // see if any of the generated attributes is PK
241
Iterator JavaDoc attributes = query.getDbEntity().getGeneratedAttributes().iterator();
242             while (attributes.hasNext()) {
243                 if (((DbAttribute) attributes.next()).isPrimaryKey()) {
244                     return true;
245                 }
246             }
247         }
248
249         return false;
250     }
251
252     /**
253      * Implements generated keys extraction supported in JDBC 3.0 specification.
254      */

255     protected void processGeneratedKeys(Statement JavaDoc statement, OperationObserver observer)
256             throws SQLException JavaDoc, CayenneException {
257
258         ResultSet JavaDoc keysRS = statement.getGeneratedKeys();
259
260         // TODO: andrus, 7/4/2007 - (1) get the type of meaningful PK's from their
261
// ObjAttributes; (2) use a different form of Statement.execute -
262
// "execute(String,String[])" to be able to map generated column names (this way
263
// we can support multiple columns.. although need to check how well this works
264
// with most common drivers)
265

266         if (this.keyRowDescriptor == null) {
267             // attempt to figure out the right descriptor from the mapping...
268
Collection JavaDoc generated = query.getDbEntity().getGeneratedAttributes();
269             if (generated.size() == 1) {
270                 DbAttribute key = (DbAttribute) generated.iterator().next();
271
272                 ColumnDescriptor[] columns = new ColumnDescriptor[1];
273
274                 // use column name from result set, but type and Java class from DB
275
// attribute
276
columns[0] = new ColumnDescriptor(keysRS.getMetaData(), 1);
277                 columns[0].setJdbcType(key.getType());
278                 columns[0].setJavaClass(TypesMapping.getJavaBySqlType(key.getType()));
279                 keyRowDescriptor = new RowDescriptor(columns, getAdapter()
280                         .getExtendedTypes());
281             }
282             else {
283                 keyRowDescriptor = new RowDescriptor(keysRS, getAdapter()
284                         .getExtendedTypes());
285             }
286         }
287
288         ResultIterator iterator = new JDBCResultIterator(
289                 null,
290                 null,
291                 keysRS,
292                 keyRowDescriptor,
293                 0);
294
295         observer.nextGeneratedDataRows(query, iterator);
296     }
297 }
298
Popular Tags