001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.core.impl;
010:
011: import com.completex.objective.components.OdalRuntimeException;
012: import com.completex.objective.components.log.Log;
013: import com.completex.objective.components.persistency.DuplicateRecordException;
014: import com.completex.objective.components.persistency.LockedRecordException;
015: import com.completex.objective.components.persistency.core.DatabasePolicy;
016: import com.completex.objective.components.persistency.transact.Transaction;
017: import com.completex.objective.components.persistency.transact.TransactionListener;
018:
019: import java.sql.CallableStatement;
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.Iterator;
025: import java.util.LinkedHashMap;
026: import java.util.LinkedList;
027: import java.util.List;
028: import java.util.Map;
029:
030: /**
031: * @author Gennady Krizhevsky
032: */
033: public class DatabaseTransactionImpl implements DatabaseTransaction {
034: // Used in multi-page selects:
035: private List multiPageResultSetWrappers;
036: // Used in multi-page selects:
037: private List multiPageStatementWrappers;
038: // Used for batch modify:
039: private Map batchModifyStatements;
040: // Return codes in modifies:
041: private List resultCodes;
042: // "Real" transaction (current one is used as a decorator)
043: private Transaction coreTransaction;
044: //
045: private DatabasePolicy databasePolicy;
046: //
047: private boolean useBatchModify;
048: //
049: private Log logger = Log.NULL_LOGGER;
050:
051: public DatabaseTransactionImpl(Transaction transaction,
052: DatabasePolicy databasePolicy, boolean useBatchModify,
053: Log logger) {
054: this .coreTransaction = transaction;
055: this .databasePolicy = databasePolicy;
056: this .useBatchModify = useBatchModify;
057:
058: setLogger(logger);
059: }
060:
061: public DatabaseTransactionImpl(Transaction transaction,
062: DatabasePolicy databasePolicy, boolean useBatchModify) {
063: this (transaction, databasePolicy, useBatchModify, null);
064: }
065:
066: public String connectionToString() {
067: return coreTransaction.connectionToString();
068: }
069:
070: public Log getLogger() {
071: return logger;
072: }
073:
074: public void setLogger(Log logger) {
075: if (logger != null) {
076: this .logger = logger;
077: }
078: }
079:
080: public void addMultiPageResultSet(
081: AbstractResultSetWrapper resultSetWrapper) {
082: if (multiPageResultSetWrappers == null) {
083: multiPageResultSetWrappers = new LinkedList();
084: }
085: multiPageResultSetWrappers.add(resultSetWrapper);
086: }
087:
088: public void addMultiPageStatement(
089: PreparedStatementWrapper statementWrapper) {
090: if (multiPageStatementWrappers == null) {
091: multiPageStatementWrappers = new LinkedList();
092: }
093: multiPageStatementWrappers.add(statementWrapper);
094: }
095:
096: public void addModifyStatement(
097: PreparedStatementWrapper statementWrapper)
098: throws SQLException {
099: statementWrapper.addBatch();
100: }
101:
102: private Map lazyModifyStatements() {
103: if (batchModifyStatements == null) {
104: batchModifyStatements = new LinkedHashMap();
105: }
106: return batchModifyStatements;
107: }
108:
109: public void close() throws SQLException {
110: if (multiPageResultSetWrappers != null
111: && !multiPageResultSetWrappers.isEmpty()) {
112: for (Iterator it = multiPageResultSetWrappers.iterator(); it
113: .hasNext();) {
114: AbstractResultSetWrapper resultSetWrapper = (AbstractResultSetWrapper) it
115: .next();
116: if (resultSetWrapper != null) {
117: resultSetWrapper.close();
118: }
119: }
120: multiPageResultSetWrappers.clear();
121: }
122: if (multiPageStatementWrappers != null
123: && !multiPageStatementWrappers.isEmpty()) {
124: for (Iterator it = multiPageStatementWrappers.iterator(); it
125: .hasNext();) {
126: PreparedStatementWrapper statementWrapper = (PreparedStatementWrapper) it
127: .next();
128: if (statementWrapper != null) {
129: statementWrapper.close();
130: }
131: }
132: multiPageStatementWrappers.clear();
133: }
134: clearBatchModify();
135: if (resultCodes != null && !resultCodes.isEmpty()) {
136: resultCodes.clear();
137: }
138: }
139:
140: private void clearBatchModify() throws SQLException {
141: if (batchModifyStatements != null
142: && !batchModifyStatements.isEmpty()) {
143: PreparedStatementWrapper statementWrapper;
144: try {
145: for (Iterator it = batchModifyStatements.keySet()
146: .iterator(); it.hasNext();) {
147: String key = (String) it.next();
148: statementWrapper = (PreparedStatementWrapper) batchModifyStatements
149: .get(key);
150: if (statementWrapper != null) {
151: statementWrapper.close();
152: }
153: }
154: } finally {
155: batchModifyStatements.clear();
156: }
157: }
158: }
159:
160: public List flush() throws SQLException {
161: try {
162: if (batchModifyStatements != null
163: && !batchModifyStatements.isEmpty()) {
164: PreparedStatementWrapper statementWrapper = null;
165: try {
166: for (Iterator it = batchModifyStatements.entrySet()
167: .iterator(); it.hasNext();) {
168: Map.Entry entry = (Map.Entry) it.next();
169: statementWrapper = (PreparedStatementWrapper) entry
170: .getValue();
171: if (statementWrapper != null) {
172: statementWrapper.executeBatch();
173: int[] rcs = statementWrapper.getRcs();
174: if (resultCodes == null) {
175: resultCodes = new LinkedList();
176: }
177: resultCodes.add(rcs);
178: }
179: }
180: } catch (SQLException e) {
181: handleSqlException(e, statementWrapper.getSql(),
182: statementWrapper.getBindBuffer(), this );
183: }
184: }
185: return resultCodes;
186: } finally {
187: clearBatchModify();
188: }
189: }
190:
191: public Statement createStatement() throws SQLException {
192: return coreTransaction.createStatement();
193: }
194:
195: public PreparedStatement prepareStatement(String sql)
196: throws SQLException {
197: if (logger != null && logger.isDebugEnabled()) {
198: logger.debug("sql: " + sql);
199: }
200: PreparedStatementWrapper statementWrapper = getModifyStatementWrapperFromCache(sql);
201: if (statementWrapper != null) {
202: return statementWrapper.getStatement();
203: } else {
204: return coreTransaction.prepareStatement(sql);
205: }
206: }
207:
208: public CallableStatement prepareCall(String sql)
209: throws SQLException {
210: PreparedStatementWrapper statementWrapper;
211: statementWrapper = getModifyStatementWrapperFromCache(sql);
212: if (statementWrapper != null) {
213: return (CallableStatement) statementWrapper.getStatement();
214: } else {
215: return coreTransaction.prepareCall(sql);
216: }
217: }
218:
219: /**
220: * Get from modify statements cache
221: *
222: * @param sql
223: * @return PreparedStatementWrapper
224: */
225: private PreparedStatementWrapper getModifyStatementWrapperFromCache(
226: String sql) {
227: PreparedStatementWrapper statementWrapper = null;
228: if (isUseBatchModify() && batchModifyStatements != null
229: && !batchModifyStatements.isEmpty()) {
230: Object o = batchModifyStatements.get(sql);
231: if (o != null) {
232: statementWrapper = (PreparedStatementWrapper) o;
233: }
234: }
235: return statementWrapper;
236: }
237:
238: public boolean isUseBatchModify() {
239: return useBatchModify;
240: }
241:
242: public PreparedStatementWrapper prepareBatchModifyStatement(
243: PreparedStatement statement,
244: BasicDatabasePersistencyImpl persistency, String sql) {
245: PreparedStatementWrapper statementWrapper = getModifyStatementWrapperFromCache(sql);
246: if (statementWrapper == null) {
247: statementWrapper = new PreparedStatementWrapper(statement,
248: this );
249: statementWrapper.setSql(sql);
250: }
251: lazyModifyStatements().put(statementWrapper.getSql(),
252: statementWrapper);
253: return statementWrapper;
254: }
255:
256: public void releaseStatement(PreparedStatement statement)
257: throws SQLException {
258: coreTransaction.releaseStatement(statement);
259: }
260:
261: public Connection getConnection() {
262: return coreTransaction.getConnection();
263: }
264:
265: /**
266: * We do not close transaction - it will be closed when commit/rollback/release happens on TransactionManager
267: * @throws SQLException
268: */
269: public void commit() throws SQLException {
270: flush();
271: coreTransaction.commit();
272: }
273:
274: /**
275: * We do not close transaction - it will be closed when commit/rollback/release happens on TransactionManager
276: * @throws SQLException
277: */
278: public void rollback() throws SQLException {
279: flush();
280: coreTransaction.rollback();
281: }
282:
283: public void commitUnchecked() {
284: try {
285: commit();
286: } catch (SQLException e) {
287: throw new OdalRuntimeException(e.getClass().getName()
288: + ": " + e.getMessage());
289: }
290: }
291:
292: public void rollbackUnchecked() {
293: try {
294: rollback();
295: } catch (SQLException e) {
296: throw new OdalRuntimeException(e.getClass().getName()
297: + ": " + e.getMessage());
298: }
299: }
300:
301: public void rollbackSilently() {
302: try {
303: rollback();
304: } catch (SQLException e) {
305: getLogger().error("Cannot rollback", e);
306: }
307: }
308:
309: private void handleSqlException(SQLException e, String sql,
310: StringBuffer bindBuffer, Transaction transaction)
311: throws SQLException {
312: String message = e.getMessage();
313: if (sql != null) {
314: message += "; sql = "
315: + sql
316: + "; bind parameters: "
317: + (bindBuffer == null ? "null" : bindBuffer
318: .toString()) + "; STATISTIC: "
319: + "connection " + transaction.getConnection()
320: + "; " + this .toString();
321: }
322: if (databasePolicy.isDuplicate(e)) {
323: throw new DuplicateRecordException(message);
324: } else if (databasePolicy.isLocked(e)) {
325: throw new LockedRecordException(message);
326: } else {
327: throw new SQLException(message);
328: }
329: }
330:
331: public void addListerner(Object key, TransactionListener listener) {
332: coreTransaction.addListerner(key, listener);
333: }
334:
335: public TransactionListener getListener(Object key) {
336: return coreTransaction.getListener(key);
337: }
338:
339: public boolean containsListener(Object key) {
340: return coreTransaction.containsListener(key);
341: }
342:
343: public void removeListerner(Object key) {
344: coreTransaction.removeListerner(key);
345: }
346:
347: public int listenersSize() {
348: return coreTransaction.listenersSize();
349: }
350:
351: public void clearListerners() {
352: }
353:
354: public Transaction getCoreTransaction() {
355: return coreTransaction;
356: }
357:
358: }
|