001: /*
002: * TableDeleterUI.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.gui.dbobjects;
013:
014: import java.awt.EventQueue;
015: import java.awt.Frame;
016: import java.awt.event.WindowEvent;
017: import java.awt.event.WindowListener;
018: import java.sql.SQLException;
019: import java.sql.Statement;
020: import java.util.ArrayList;
021: import java.util.List;
022: import javax.swing.JDialog;
023: import javax.swing.JOptionPane;
024: import workbench.db.TableIdentifier;
025: import workbench.db.WbConnection;
026: import workbench.db.importer.TableDependencySorter;
027: import workbench.gui.WbSwingUtilities;
028: import workbench.gui.components.EditWindow;
029: import workbench.gui.components.NoSelectionModel;
030: import workbench.gui.components.WbButton;
031: import workbench.interfaces.TableDeleteListener;
032: import workbench.log.LogMgr;
033: import workbench.resource.ResourceMgr;
034: import workbench.util.ExceptionUtil;
035: import workbench.util.SqlUtil;
036: import workbench.util.WbThread;
037:
038: /**
039: *
040: * @author support@sql-workbench.net
041: */
042: public class TableDeleterUI extends javax.swing.JPanel implements
043: WindowListener {
044: private JDialog dialog;
045: private List<TableIdentifier> objectNames;
046: private boolean cancelled;
047: private WbConnection connection;
048: private Thread deleteThread;
049: private Thread checkThread;
050: private List<TableDeleteListener> deleteListener;
051: private Statement currentStatement;
052:
053: public TableDeleterUI() {
054: initComponents();
055: }
056:
057: /** This method is called from within the constructor to
058: * initialize the form.
059: * WARNING: Do NOT modify this code. The content of this method is
060: * always regenerated by the Form Editor.
061: */
062: // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
063: private void initComponents() {
064: java.awt.GridBagConstraints gridBagConstraints;
065:
066: buttonGroup1 = new javax.swing.ButtonGroup();
067: buttonPanel = new javax.swing.JPanel();
068: deleteButton = new WbButton();
069: cancelButton = new WbButton();
070: mainPanel = new javax.swing.JPanel();
071: jScrollPane1 = new javax.swing.JScrollPane();
072: objectList = new javax.swing.JList();
073: optionPanel = new javax.swing.JPanel();
074: statusLabel = new javax.swing.JLabel();
075: jPanel1 = new javax.swing.JPanel();
076: checkFKButton = new javax.swing.JButton();
077: jPanel2 = new javax.swing.JPanel();
078: commitEach = new javax.swing.JRadioButton();
079: commitAtEnd = new javax.swing.JRadioButton();
080: useTruncateCheckBox = new javax.swing.JCheckBox();
081: jPanel3 = new javax.swing.JPanel();
082: showScript = new javax.swing.JButton();
083: addMissingTables = new javax.swing.JCheckBox();
084:
085: setLayout(new java.awt.BorderLayout());
086:
087: buttonPanel.setLayout(new java.awt.FlowLayout(
088: java.awt.FlowLayout.RIGHT));
089:
090: deleteButton.setText(ResourceMgr
091: .getString("LblDeleteTableData"));
092: deleteButton
093: .addActionListener(new java.awt.event.ActionListener() {
094: public void actionPerformed(
095: java.awt.event.ActionEvent evt) {
096: deleteButtonActionPerformed(evt);
097: }
098: });
099: buttonPanel.add(deleteButton);
100:
101: cancelButton.setText(ResourceMgr.getString("LblCancel"));
102: cancelButton
103: .addActionListener(new java.awt.event.ActionListener() {
104: public void actionPerformed(
105: java.awt.event.ActionEvent evt) {
106: cancelButtonActionPerformed(evt);
107: }
108: });
109: buttonPanel.add(cancelButton);
110:
111: add(buttonPanel, java.awt.BorderLayout.SOUTH);
112:
113: mainPanel.setLayout(new java.awt.BorderLayout(0, 5));
114:
115: objectList
116: .setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
117: objectList.setSelectionModel(new NoSelectionModel());
118: jScrollPane1.setViewportView(objectList);
119:
120: mainPanel.add(jScrollPane1, java.awt.BorderLayout.CENTER);
121:
122: optionPanel.setLayout(new java.awt.BorderLayout(0, 5));
123:
124: statusLabel.setBorder(javax.swing.BorderFactory
125: .createEtchedBorder());
126: statusLabel.setMaximumSize(new java.awt.Dimension(32768, 24));
127: statusLabel.setMinimumSize(new java.awt.Dimension(150, 24));
128: statusLabel.setPreferredSize(new java.awt.Dimension(150, 24));
129: optionPanel.add(statusLabel, java.awt.BorderLayout.SOUTH);
130:
131: jPanel1.setLayout(new java.awt.GridBagLayout());
132:
133: checkFKButton.setText(ResourceMgr.getString("LblCheckFKDeps"));
134: checkFKButton.setToolTipText(ResourceMgr
135: .getDescription("LblCheckFKDeps"));
136: checkFKButton
137: .addActionListener(new java.awt.event.ActionListener() {
138: public void actionPerformed(
139: java.awt.event.ActionEvent evt) {
140: checkFKButtonActionPerformed(evt);
141: }
142: });
143: gridBagConstraints = new java.awt.GridBagConstraints();
144: gridBagConstraints.gridx = 0;
145: gridBagConstraints.gridy = 0;
146: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
147: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
148: gridBagConstraints.weighty = 1.0;
149: gridBagConstraints.insets = new java.awt.Insets(6, 8, 0, 5);
150: jPanel1.add(checkFKButton, gridBagConstraints);
151:
152: jPanel2.setLayout(new java.awt.GridBagLayout());
153:
154: buttonGroup1.add(commitEach);
155: commitEach.setSelected(true);
156: commitEach.setText(ResourceMgr
157: .getString("LblCommitEachTableDelete"));
158: commitEach.setBorder(null);
159: gridBagConstraints = new java.awt.GridBagConstraints();
160: gridBagConstraints.gridx = 0;
161: gridBagConstraints.gridy = 0;
162: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
163: gridBagConstraints.weightx = 1.0;
164: gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
165: jPanel2.add(commitEach, gridBagConstraints);
166:
167: buttonGroup1.add(commitAtEnd);
168: commitAtEnd.setText(ResourceMgr
169: .getString("LblCommitTableDeleteAtEnd"));
170: commitAtEnd.setBorder(null);
171: gridBagConstraints = new java.awt.GridBagConstraints();
172: gridBagConstraints.gridx = 0;
173: gridBagConstraints.gridy = 1;
174: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
175: gridBagConstraints.weightx = 1.0;
176: gridBagConstraints.insets = new java.awt.Insets(4, 6, 0, 0);
177: jPanel2.add(commitAtEnd, gridBagConstraints);
178:
179: useTruncateCheckBox.setText(ResourceMgr
180: .getString("LblUseTruncate"));
181: useTruncateCheckBox.setBorder(null);
182: useTruncateCheckBox
183: .addItemListener(new java.awt.event.ItemListener() {
184: public void itemStateChanged(
185: java.awt.event.ItemEvent evt) {
186: useTruncateCheckBoxItemStateChanged(evt);
187: }
188: });
189: gridBagConstraints = new java.awt.GridBagConstraints();
190: gridBagConstraints.gridx = 0;
191: gridBagConstraints.gridy = 2;
192: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
193: gridBagConstraints.weightx = 1.0;
194: gridBagConstraints.insets = new java.awt.Insets(6, 6, 0, 0);
195: jPanel2.add(useTruncateCheckBox, gridBagConstraints);
196: gridBagConstraints = new java.awt.GridBagConstraints();
197: gridBagConstraints.gridx = 0;
198: gridBagConstraints.gridy = 3;
199: gridBagConstraints.weightx = 1.0;
200: gridBagConstraints.weighty = 1.0;
201: jPanel2.add(jPanel3, gridBagConstraints);
202:
203: gridBagConstraints = new java.awt.GridBagConstraints();
204: gridBagConstraints.gridx = 2;
205: gridBagConstraints.gridy = 0;
206: gridBagConstraints.gridheight = 3;
207: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
208: gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
209: gridBagConstraints.weightx = 1.0;
210: gridBagConstraints.weighty = 1.0;
211: gridBagConstraints.insets = new java.awt.Insets(6, 4, 0, 15);
212: jPanel1.add(jPanel2, gridBagConstraints);
213:
214: showScript.setText(ResourceMgr.getString("LblShowScript"));
215: showScript
216: .addActionListener(new java.awt.event.ActionListener() {
217: public void actionPerformed(
218: java.awt.event.ActionEvent evt) {
219: showScriptActionPerformed(evt);
220: }
221: });
222: gridBagConstraints = new java.awt.GridBagConstraints();
223: gridBagConstraints.gridx = 0;
224: gridBagConstraints.gridy = 2;
225: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
226: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
227: gridBagConstraints.weighty = 1.0;
228: gridBagConstraints.insets = new java.awt.Insets(9, 8, 0, 5);
229: jPanel1.add(showScript, gridBagConstraints);
230:
231: addMissingTables.setSelected(true);
232: addMissingTables.setText(ResourceMgr
233: .getString("LblIncFkTables"));
234: addMissingTables.setToolTipText(ResourceMgr
235: .getDescription("LblIncFkTables"));
236: gridBagConstraints = new java.awt.GridBagConstraints();
237: gridBagConstraints.gridx = 0;
238: gridBagConstraints.gridy = 1;
239: gridBagConstraints.gridwidth = 2;
240: gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
241: gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
242: gridBagConstraints.weighty = 1.0;
243: gridBagConstraints.insets = new java.awt.Insets(0, 7, 0, 0);
244: jPanel1.add(addMissingTables, gridBagConstraints);
245:
246: optionPanel.add(jPanel1, java.awt.BorderLayout.CENTER);
247:
248: mainPanel.add(optionPanel, java.awt.BorderLayout.SOUTH);
249:
250: add(mainPanel, java.awt.BorderLayout.CENTER);
251: }// </editor-fold>//GEN-END:initComponents
252:
253: private void useTruncateCheckBoxItemStateChanged(
254: java.awt.event.ItemEvent evt)//GEN-FIRST:event_useTruncateCheckBoxItemStateChanged
255: {//GEN-HEADEREND:event_useTruncateCheckBoxItemStateChanged
256: if (this .useTruncateCheckBox.isSelected()) {
257: this .disableCommitSettings();
258: } else if (!this .connection.getAutoCommit()) {
259: this .enableCommitSettings();
260: }
261: }//GEN-LAST:event_useTruncateCheckBoxItemStateChanged
262:
263: private void cancelButtonActionPerformed(
264: java.awt.event.ActionEvent evt)//GEN-FIRST:event_cancelButtonActionPerformed
265: {//GEN-HEADEREND:event_cancelButtonActionPerformed
266: this .cancelled = true;
267: closeWindow();
268: }//GEN-LAST:event_cancelButtonActionPerformed
269:
270: private void deleteButtonActionPerformed(
271: java.awt.event.ActionEvent evt)//GEN-FIRST:event_deleteButtonActionPerformed
272: {//GEN-HEADEREND:event_deleteButtonActionPerformed
273: this .startDelete();
274: }//GEN-LAST:event_deleteButtonActionPerformed
275:
276: private void checkFKButtonActionPerformed(
277: java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkFKButtonActionPerformed
278:
279: if (this .connection.isBusy())
280: return;
281:
282: this .deleteButton.setEnabled(false);
283: this .showScript.setEnabled(false);
284: this .statusLabel.setText(ResourceMgr.getString("MsgFkDeps"));
285:
286: WbSwingUtilities.showWaitCursor(dialog);
287:
288: this .checkThread = new WbThread("FKCheck") {
289: public void run() {
290: List<TableIdentifier> sorted = null;
291: try {
292: connection.setBusy(true);
293: TableDependencySorter sorter = new TableDependencySorter(
294: connection);
295: sorted = sorter.sortForDelete(objectNames,
296: addMissingTables.isSelected());
297: } catch (Exception e) {
298: LogMgr.logError("TableDeleterUI.checkFK()",
299: "Error checking FK dependencies", e);
300: WbSwingUtilities.showErrorMessage(ExceptionUtil
301: .getDisplay(e));
302: sorted = null;
303: } finally {
304: connection.setBusy(false);
305: fkCheckFinished(sorted);
306: }
307: }
308: };
309:
310: checkThread.start();
311: }//GEN-LAST:event_checkFKButtonActionPerformed
312:
313: private void showScriptActionPerformed(
314: java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showScriptActionPerformed
315: showScript();
316: }//GEN-LAST:event_showScriptActionPerformed
317:
318: protected void fkCheckFinished(final List<TableIdentifier> newlist) {
319: this .checkThread = null;
320: EventQueue.invokeLater(new Runnable() {
321: public void run() {
322: statusLabel.setText("");
323: if (newlist != null) {
324: setObjects(newlist);
325: }
326: deleteButton.setEnabled(true);
327: showScript.setEnabled(true);
328: WbSwingUtilities.showDefaultCursor(dialog);
329: }
330: });
331: }
332:
333: protected void closeWindow() {
334: try {
335: if (this .deleteThread != null) {
336: this .deleteThread.interrupt();
337: this .deleteThread = null;
338: }
339: } catch (Exception e) {
340: LogMgr.logWarning("TableDeleterUI.cancel()",
341: "Error when trying to kill delete Thread", e);
342: }
343:
344: try {
345: if (this .checkThread != null) {
346: this .checkThread.interrupt();
347: this .checkThread = null;
348: }
349: } catch (Exception e) {
350: LogMgr.logWarning("TableDeleterUI.cancel()",
351: "Error when trying to kill check thread", e);
352: }
353:
354: this .dialog.setVisible(false);
355: // this.dialog.dispose();
356: this .dialog = null;
357: }
358:
359: public void setConnection(WbConnection aConn) {
360: this .connection = aConn;
361: if (this .connection != null) {
362:
363: this .useTruncateCheckBox.setEnabled(this .connection
364: .getDbSettings().supportsTruncate());
365: boolean autoCommit = this .connection.getAutoCommit();
366: if (autoCommit) {
367: this .disableCommitSettings();
368: } else {
369: this .enableCommitSettings();
370: }
371: }
372: }
373:
374: protected void disableCommitSettings() {
375: this .commitAtEnd.setEnabled(false);
376: this .commitEach.setEnabled(false);
377: }
378:
379: protected void enableCommitSettings() {
380: this .commitAtEnd.setEnabled(true);
381: this .commitEach.setEnabled(true);
382: }
383:
384: protected void startDelete() {
385: this .deleteThread = new WbThread("TableDeleteThread") {
386: public void run() {
387: doDelete();
388: }
389: };
390: this .deleteThread.start();
391: }
392:
393: protected void doDelete() {
394: this .cancelled = false;
395: boolean ignoreAll = false;
396:
397: boolean doCommitEach = this .commitEach.isSelected();
398: boolean useTruncate = this .useTruncateCheckBox.isSelected();
399: if (useTruncate) {
400: doCommitEach = false;
401: }
402: boolean hasError = false;
403: List<TableIdentifier> tables = new ArrayList<TableIdentifier>();
404: int count = this .objectNames.size();
405: TableIdentifier table = null;
406:
407: try {
408: this .currentStatement = this .connection.createStatement();
409: } catch (SQLException e) {
410: WbSwingUtilities.showErrorMessage(e.getMessage());
411: LogMgr.logError("TableDeleterUI.doDelete()",
412: "Error creating statement", e);
413: return;
414: }
415:
416: try {
417: this .connection.setBusy(true);
418:
419: for (int i = 0; i < count; i++) {
420: if (this .cancelled) {
421: break;
422: }
423: table = this .objectNames.get(i);
424: this .statusLabel.setText(ResourceMgr
425: .getString("TxtDeletingTable")
426: + " " + table + " ...");
427: try {
428: this .deleteTable(table, useTruncate, doCommitEach);
429: tables.add(table);
430: } catch (Exception ex) {
431: String error = ExceptionUtil.getDisplay(ex);
432: LogMgr.logError("TableDeleterUI.doDelete()",
433: "Error deleting table " + table, ex);
434:
435: if (!ignoreAll) {
436: String question = ResourceMgr
437: .getString("ErrDeleteTableData");
438: question = question.replaceAll("%table%", table
439: .toString());
440: question = question
441: .replaceAll("%error%", error);
442: question = question + "\n"
443: + ResourceMgr.getString("MsgContinueQ");
444:
445: int choice = WbSwingUtilities
446: .getYesNoIgnoreAll(this .dialog,
447: question);
448: if (choice == JOptionPane.NO_OPTION) {
449: // the hasError flag will cause a rollback at the end.
450: hasError = true;
451: break;
452: }
453: if (choice == WbSwingUtilities.IGNORE_ALL) {
454: // if we ignore all errors we should do a commit at the
455: // end in order to ensure that the delete's which were
456: // successful are committed.
457: hasError = false;
458: ignoreAll = true;
459: }
460: }
461: }
462: }
463:
464: boolean doCommit = true && !cancelled;
465: try {
466: if (!doCommitEach) {
467: if (hasError || cancelled) {
468: doCommit = false;
469: this .connection.rollback();
470: } else {
471: this .connection.commit();
472: }
473: }
474: } catch (SQLException e) {
475: LogMgr.logError("TableDeleterUI.doDelete()",
476: "Error on commit/rollback", e);
477: String msg = null;
478:
479: if (doCommit) {
480: ResourceMgr.getString("ErrCommit");
481: } else {
482: msg = ResourceMgr.getString("ErrRollbackTableData");
483: }
484: msg = msg.replaceAll("%error%", e.getMessage());
485:
486: WbSwingUtilities.showErrorMessage(this .dialog, msg);
487: }
488: } finally {
489: SqlUtil.closeStatement(currentStatement);
490: this .connection.setBusy(false);
491: }
492:
493: this .fireTableDeleted(tables);
494:
495: this .statusLabel.setText("");
496: if (!hasError) {
497: this .closeWindow();
498: }
499: }
500:
501: protected void deleteTable(final TableIdentifier table,
502: final boolean useTruncate, final boolean doCommit)
503: throws SQLException {
504: try {
505: String deleteSql = getDeleteStatement(table, useTruncate);
506: LogMgr.logInfo("TableDeleterUI.deleteTable()",
507: "Executing: [" + deleteSql
508: + "] to delete target table...");
509: currentStatement.executeUpdate(deleteSql);
510: if (doCommit && !this .connection.getAutoCommit()) {
511: this .connection.commit();
512: }
513: } catch (SQLException e) {
514: if (doCommit && !this .connection.getAutoCommit()) {
515: this .connection.rollback();
516: }
517: LogMgr.logError("TableDeleterUI.deleteTable()",
518: "Error when deleting table!", e);
519: throw e;
520: }
521: }
522:
523: protected String getDeleteStatement(final TableIdentifier table,
524: final boolean useTruncate) {
525: String deleteSql = null;
526: String tableName = table.getTableExpression(this .connection);
527: if (useTruncate) {
528: deleteSql = "TRUNCATE TABLE " + tableName;
529: } else {
530: deleteSql = "DELETE FROM " + tableName;
531: }
532: return deleteSql;
533: }
534:
535: protected void showScript() {
536: boolean doCommitEach = this .commitEach.isSelected();
537: boolean useTruncate = this .useTruncateCheckBox.isSelected();
538: if (useTruncate) {
539: doCommitEach = false;
540: }
541: StringBuilder script = new StringBuilder(
542: objectNames.size() * 30);
543: for (TableIdentifier table : objectNames) {
544: String sql = this .getDeleteStatement(table, useTruncate);
545: script.append(sql);
546: script.append(";\n");
547: if (doCommitEach) {
548: script.append("COMMIT;\n\n");
549: }
550: }
551:
552: if (!doCommitEach) {
553: script.append("\nCOMMIT;\n");
554: }
555:
556: final EditWindow w = new EditWindow(this .dialog, ResourceMgr
557: .getString("TxtWindowTitleGeneratedScript"), script
558: .toString(), "workbench.tabledeleter.scriptwindow",
559: true);
560: w.setVisible(true);
561: w.dispose();
562: }
563:
564: public boolean dialogWasCancelled() {
565: return this .cancelled;
566: }
567:
568: public void setObjects(List<TableIdentifier> objects) {
569: this .objectNames = objects;
570: int numNames = this .objectNames.size();
571:
572: String[] display = new String[numNames];
573: for (int i = 0; i < numNames; i++) {
574: display[i] = this .objectNames.get(i).toString();
575: }
576: this .objectList.setListData(display);
577: }
578:
579: public void showDialog(Frame aParent) {
580: this .dialog = new JDialog(aParent, ResourceMgr
581: .getString("TxtDeleteTableData"), false);
582: this .dialog.getContentPane().add(this );
583: this .dialog.pack();
584: if (this .dialog.getWidth() < 200) {
585: this .dialog.setSize(200, this .dialog.getHeight());
586: }
587: WbSwingUtilities.center(this .dialog, aParent);
588: this .cancelled = true;
589: this .dialog.setVisible(true);
590: }
591:
592: public void addDeleteListener(TableDeleteListener listener) {
593: if (this .deleteListener == null) {
594: this .deleteListener = new ArrayList<TableDeleteListener>();
595: }
596: this .deleteListener.add(listener);
597: }
598:
599: public void removeDeleteListener(TableDeleteListener listener) {
600: if (this .deleteListener == null) {
601: return;
602: }
603: this .deleteListener.remove(listener);
604: }
605:
606: protected void fireTableDeleted(List tables) {
607: if (this .deleteListener == null) {
608: return;
609: }
610: for (TableDeleteListener l : this .deleteListener) {
611: l.tableDataDeleted(tables);
612: }
613: }
614:
615: public void windowActivated(WindowEvent e) {
616: }
617:
618: public void windowClosed(WindowEvent e) {
619: }
620:
621: public void windowClosing(WindowEvent e) {
622: this .cancelled = true;
623: closeWindow();
624: }
625:
626: public void windowDeactivated(WindowEvent e) {
627: }
628:
629: public void windowDeiconified(WindowEvent e) {
630: }
631:
632: public void windowIconified(WindowEvent e) {
633: }
634:
635: public void windowOpened(WindowEvent e) {
636: }
637:
638: // Variables declaration - do not modify//GEN-BEGIN:variables
639: public javax.swing.JCheckBox addMissingTables;
640: public javax.swing.ButtonGroup buttonGroup1;
641: public javax.swing.JPanel buttonPanel;
642: public javax.swing.JButton cancelButton;
643: public javax.swing.JButton checkFKButton;
644: public javax.swing.JRadioButton commitAtEnd;
645: public javax.swing.JRadioButton commitEach;
646: public javax.swing.JButton deleteButton;
647: public javax.swing.JPanel jPanel1;
648: public javax.swing.JPanel jPanel2;
649: public javax.swing.JPanel jPanel3;
650: public javax.swing.JScrollPane jScrollPane1;
651: public javax.swing.JPanel mainPanel;
652: public javax.swing.JList objectList;
653: public javax.swing.JPanel optionPanel;
654: public javax.swing.JButton showScript;
655: public javax.swing.JLabel statusLabel;
656: public javax.swing.JCheckBox useTruncateCheckBox;
657: // End of variables declaration//GEN-END:variables
658: }
|