001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc2.schema;
023:
024: import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMRFieldBridge2;
025: import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMPFieldBridge2;
026: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
027: import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
028: import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil;
029: import org.jboss.deployment.DeploymentException;
030: import org.jboss.logging.Logger;
031:
032: import javax.sql.DataSource;
033: import javax.transaction.Transaction;
034: import java.sql.SQLException;
035: import java.sql.Connection;
036: import java.sql.PreparedStatement;
037:
038: /**
039: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
040: * @version <tt>$Revision: 57209 $</tt>
041: */
042: public class RelationTable implements Table {
043: private static final byte CREATED = 1;
044: private static final byte DELETED = 2;
045:
046: private final Schema schema;
047: private final int tableId;
048: private final DataSource ds;
049: private final String tableName;
050: private final JDBCCMRFieldBridge2 leftField;
051: private final JDBCCMRFieldBridge2 rightField;
052: private final Logger log;
053:
054: private String insertSql;
055: private String deleteSql;
056:
057: public RelationTable(JDBCCMRFieldBridge2 leftField,
058: JDBCCMRFieldBridge2 rightField, Schema schema, int tableId)
059: throws DeploymentException {
060: this .schema = schema;
061: this .tableId = tableId;
062: this .leftField = leftField;
063: this .rightField = rightField;
064:
065: JDBCRelationMetaData metadata = leftField.getMetaData()
066: .getRelationMetaData();
067: ds = metadata.getDataSource();
068: tableName = SQLUtil.fixTableName(
069: metadata.getDefaultTableName(), ds);
070:
071: log = Logger.getLogger(getClass().getName() + "." + tableName);
072:
073: // generate sql
074:
075: insertSql = "insert into " + tableName + " (";
076:
077: JDBCCMPFieldBridge2[] keyFields = (JDBCCMPFieldBridge2[]) this .leftField
078: .getTableKeyFields();
079: insertSql += keyFields[0].getColumnName();
080: for (int i = 1; i < keyFields.length; ++i) {
081: insertSql += ", " + keyFields[i].getColumnName();
082: }
083:
084: keyFields = (JDBCCMPFieldBridge2[]) this .rightField
085: .getTableKeyFields();
086: insertSql += ", " + keyFields[0].getColumnName();
087: for (int i = 1; i < keyFields.length; ++i) {
088: insertSql += ", " + keyFields[i].getColumnName();
089: }
090:
091: insertSql += ") values (?";
092: for (int i = 1; i < this .leftField.getTableKeyFields().length
093: + this .rightField.getTableKeyFields().length; ++i) {
094: insertSql += ", ?";
095: }
096:
097: insertSql += ")";
098:
099: log.debug("insert sql: " + insertSql);
100:
101: deleteSql = "delete from " + tableName + " where ";
102: keyFields = (JDBCCMPFieldBridge2[]) this .leftField
103: .getTableKeyFields();
104: deleteSql += keyFields[0].getColumnName() + "=?";
105: for (int i = 1; i < keyFields.length; ++i) {
106: deleteSql += " and " + keyFields[i].getColumnName() + "=?";
107: }
108:
109: keyFields = (JDBCCMPFieldBridge2[]) this .rightField
110: .getTableKeyFields();
111: deleteSql += " and " + keyFields[0].getColumnName() + "=?";
112: for (int i = 1; i < keyFields.length; ++i) {
113: deleteSql += " and " + keyFields[i].getColumnName() + "=?";
114: }
115:
116: log.debug("delete sql: " + deleteSql);
117: }
118:
119: // Public
120:
121: public void addRelation(JDBCCMRFieldBridge2 field1, Object key1,
122: JDBCCMRFieldBridge2 field2, Object key2) {
123: View view = getView();
124: if (field1 == leftField) {
125: view.addKeys(key1, key2);
126: } else {
127: view.addKeys(key2, key1);
128: }
129: }
130:
131: public void removeRelation(JDBCCMRFieldBridge2 field1, Object key1,
132: JDBCCMRFieldBridge2 field2, Object key2) {
133: View view = getView();
134: if (field1 == leftField) {
135: view.removeKeys(key1, key2);
136: } else {
137: view.removeKeys(key2, key1);
138: }
139: }
140:
141: // Table implementation
142:
143: public int getTableId() {
144: return tableId;
145: }
146:
147: public String getTableName() {
148: return tableName;
149: }
150:
151: public Table.View createView(Transaction tx) {
152: return new View();
153: }
154:
155: // Private
156:
157: private void delete(View view) throws SQLException {
158: if (view.deleted == null) {
159: if (log.isTraceEnabled()) {
160: log.trace("no rows to delete");
161: }
162: return;
163: }
164:
165: Connection con = null;
166: PreparedStatement ps = null;
167: try {
168: if (log.isDebugEnabled()) {
169: log.debug("executing : " + deleteSql);
170: }
171:
172: con = ds.getConnection();
173: ps = con.prepareStatement(deleteSql);
174:
175: int batchCount = 0;
176: while (view.deleted != null) {
177: RelationKeys keys = view.deleted;
178:
179: int paramInd = 1;
180: JDBCCMPFieldBridge2[] keyFields = (JDBCCMPFieldBridge2[]) leftField
181: .getTableKeyFields();
182: for (int pkInd = 0; pkInd < keyFields.length; ++pkInd) {
183: JDBCCMPFieldBridge2 pkField = keyFields[pkInd];
184: Object fieldValue = pkField
185: .getPrimaryKeyValue(keys.leftKey);
186: paramInd = pkField.setArgumentParameters(ps,
187: paramInd, fieldValue);
188: }
189:
190: keyFields = (JDBCCMPFieldBridge2[]) rightField
191: .getTableKeyFields();
192: for (int pkInd = 0; pkInd < keyFields.length; ++pkInd) {
193: JDBCCMPFieldBridge2 pkField = keyFields[pkInd];
194: Object fieldValue = pkField
195: .getPrimaryKeyValue(keys.rightKey);
196: paramInd = pkField.setArgumentParameters(ps,
197: paramInd, fieldValue);
198: }
199:
200: ps.addBatch();
201: ++batchCount;
202:
203: keys.dereference();
204: }
205:
206: ps.executeBatch();
207:
208: if (view.deleted != null) {
209: throw new IllegalStateException(
210: "There are still rows to delete!");
211: }
212:
213: if (log.isTraceEnabled()) {
214: log.trace("deleted rows: " + batchCount);
215: }
216: } catch (SQLException e) {
217: log.error("Failed to delete view: " + e.getMessage(), e);
218: throw e;
219: } finally {
220: JDBCUtil.safeClose(ps);
221: JDBCUtil.safeClose(con);
222: }
223: }
224:
225: private void insert(View view) throws SQLException {
226: if (view.created == null) {
227: if (log.isTraceEnabled()) {
228: log.trace("no rows to insert");
229: }
230: return;
231: }
232:
233: Connection con = null;
234: PreparedStatement ps = null;
235: try {
236: if (log.isDebugEnabled()) {
237: log.debug("executing : " + insertSql);
238: }
239:
240: con = ds.getConnection();
241: ps = con.prepareStatement(insertSql);
242:
243: int batchCount = 0;
244: while (view.created != null) {
245: RelationKeys keys = view.created;
246:
247: JDBCCMPFieldBridge2[] keyFields = (JDBCCMPFieldBridge2[]) leftField
248: .getTableKeyFields();
249: int paramInd = 1;
250: for (int fInd = 0; fInd < keyFields.length; ++fInd) {
251: JDBCCMPFieldBridge2 field = keyFields[fInd];
252: Object fieldValue = field
253: .getPrimaryKeyValue(keys.leftKey);
254: paramInd = field.setArgumentParameters(ps,
255: paramInd, fieldValue);
256: }
257:
258: keyFields = (JDBCCMPFieldBridge2[]) rightField
259: .getTableKeyFields();
260: for (int fInd = 0; fInd < keyFields.length; ++fInd) {
261: JDBCCMPFieldBridge2 field = keyFields[fInd];
262: Object fieldValue = field
263: .getPrimaryKeyValue(keys.rightKey);
264: paramInd = field.setArgumentParameters(ps,
265: paramInd, fieldValue);
266: }
267:
268: ps.addBatch();
269: ++batchCount;
270:
271: keys.dereference();
272: }
273:
274: ps.executeBatch();
275:
276: if (log.isTraceEnabled()) {
277: log.trace("inserted rows: " + batchCount);
278: }
279: } catch (SQLException e) {
280: log
281: .error("Failed to insert new rows: "
282: + e.getMessage(), e);
283: throw e;
284: } finally {
285: JDBCUtil.safeClose(ps);
286: JDBCUtil.safeClose(con);
287: }
288: }
289:
290: private View getView() {
291: return (View) schema.getView(this );
292: }
293:
294: // Inner
295:
296: private class View implements Table.View {
297: private RelationKeys created;
298: private RelationKeys deleted;
299:
300: // Public
301:
302: public void addKeys(Object leftKey, Object rightKey) {
303: // if it was deleted then dereference
304: RelationKeys keys = deleted;
305: while (keys != null) {
306: if (keys.equals(leftKey, rightKey)) {
307: keys.dereference();
308: return;
309: }
310: keys = keys.next;
311: }
312:
313: // add to created
314: keys = new RelationKeys(this , leftKey, rightKey);
315:
316: if (created != null) {
317: keys.next = created;
318: created.prev = keys;
319: }
320: created = keys;
321: keys.state = CREATED;
322: }
323:
324: public void removeKeys(Object leftKey, Object rightKey) {
325: // if it was created then dereference
326: RelationKeys keys = created;
327: while (keys != null) {
328: if (keys.equals(leftKey, rightKey)) {
329: keys.dereference();
330: return;
331: }
332: keys = keys.next;
333: }
334:
335: // add to deleted
336: keys = new RelationKeys(this , leftKey, rightKey);
337:
338: if (deleted != null) {
339: keys.next = deleted;
340: deleted.prev = keys;
341: }
342: deleted = keys;
343: keys.state = DELETED;
344: }
345:
346: // Table.View implementation
347:
348: public void flushDeleted(Schema.Views views)
349: throws SQLException {
350: delete(this );
351: }
352:
353: public void flushCreated(Schema.Views views)
354: throws SQLException {
355: insert(this );
356: }
357:
358: public void flushUpdated() throws SQLException {
359: }
360:
361: public void beforeCompletion() {
362: }
363:
364: public void committed() {
365: }
366:
367: public void rolledback() {
368: }
369: }
370:
371: private class RelationKeys {
372: private final View view;
373: private final Object leftKey;
374: private final Object rightKey;
375:
376: private byte state;
377: private RelationKeys next;
378: private RelationKeys prev;
379:
380: public RelationKeys(View view, Object leftKey, Object rightKey) {
381: this .view = view;
382: this .leftKey = leftKey;
383: this .rightKey = rightKey;
384: }
385:
386: // Public
387:
388: public boolean equals(Object leftKey, Object rightKey) {
389: return this .leftKey.equals(leftKey)
390: && this .rightKey.equals(rightKey);
391: }
392:
393: public void dereference() {
394: if (state == CREATED && this == view.created) {
395: view.created = next;
396: } else if (state == DELETED && this == view.deleted) {
397: view.deleted = next;
398: }
399:
400: if (next != null) {
401: next.prev = prev;
402: }
403:
404: if (prev != null) {
405: prev.next = next;
406: }
407:
408: next = null;
409: prev = null;
410: }
411: }
412: }
|