<output id="r87xx"></output>
    1. 
      
      <mark id="r87xx"><thead id="r87xx"><input id="r87xx"></input></thead></mark>
        •   

               當前位置:首頁>軟件介紹>MySQL管理器 查詢:
               
          MySQL管理器

          當前IT行業(yè)流行的關系型數(shù)據(jù)庫有Oracle、SQL Server、DB2和MySQL等。其中MySQL是一個小型的關系型數(shù)據(jù)庫,開發(fā)者是瑞典的MySQL AB公司,于2008年6月1日被Sun公司收購。MySQL擁有體積小、速度快等優(yōu)點,被廣泛的應用在中小型企業(yè)的IT系統(tǒng)中,更重要的一點是,它是開源的。由于MySQL應用廣泛,因此涌現(xiàn)出許多MySQL的客戶端,例如MySQL Front、Navicat與MySQL自帶的MySQL Administrator等,這些都是我們平時在開發(fā)MySQL數(shù)據(jù)庫應用時十分常用的MySQL圖形管理工具。這些優(yōu)秀的工具為我們提供了十分方便的功能去管理MySQL數(shù)據(jù)庫,例如提供瀏覽數(shù)據(jù)的圖形界面、操作數(shù)據(jù)的界面、操作各個數(shù)據(jù)庫元素(表、視圖、存儲過程等)的界面,這些功能為我們帶來了極大的方便,可以在一個完全圖形化的界面進行數(shù)據(jù)庫處理。使用這些工具,你可以不懂如何編寫SQL語句,只需要通過操作圖形界面,就可以達到操作數(shù)據(jù)庫的目的。

          在本章中,我們將自己開發(fā)一個簡單的MySQL管理器。在開發(fā)的過程中,讓大家了解前面所講到的那些優(yōu)秀工具的實現(xiàn)原理。在本章開頭已經(jīng)提到,這些管理工具,提供了各種的圖形界面讓我們?nèi)ミM行各種的操作,因此,開發(fā)一個MySQL管理器,除了需要編寫一些操作數(shù)據(jù)庫的SQL以外,還需要注意的是圖形界面的處理。這些管理工具,實現(xiàn)的原理并無太大差別,但是哪個工具更能得到多數(shù)使用者的青睞,更多的就是取決于這些工具給用戶帶來的使用體驗及方便性。

          本章所開發(fā)的MySQL管理器是基于MySQL5.0開發(fā)的,因此如果要得到最佳的運行效果,請使用MySQL5.0。由于MySQL各個版本間都存在差別,例如筆者在開發(fā)這個管理器的時候,就遇到MySQL5.0與MySQL5.1之間的微小差別,這些差別對我們開發(fā)所產(chǎn)生的影響,將在下面的章節(jié)中詳細介紹。

          1 MySQL管理器原理

          MySQL管理器,主要功能是讓用戶可以輕松進行各種的MySQL操作,包括連接管理、數(shù)據(jù)庫管理、表管理、視圖管理、存儲過程和函數(shù)管理,這些功能點我們都可以使用JDBC實現(xiàn),例如表管理中包括創(chuàng)建表、修改表等功能,我們可以使用JDBC直接執(zhí)行SQL語句中的CREATE TABLE和ALTER TABLE來達到目的。除了這些功能外,還需要對數(shù)據(jù)庫中的數(shù)據(jù)進行導出和導與的操作,進行這些操作,我們可以編寫程序來實現(xiàn),但是,更好辦法就是使用MySQL的命令(mysql或者mysqldump)來解決,這樣可以輕松解決數(shù)據(jù)的導出與導入,但是,前提就是使用的客戶端必須安裝MySQL數(shù)據(jù)庫,并且要告訴我們這個管理器,MySQL的具體目錄,我們可以使用程序去調(diào)用這些MySQL的命令。下面,我們就開始實現(xiàn)這些所定義的功能。

          2 建立界面

          在編寫程序前,我們需要準備各個界面,包括連接管理界面、表管理界面、視圖管理界面、存儲過程(函數(shù))管理界面與查看數(shù)據(jù)界面等。表管理、視圖管理、存儲過程和函數(shù)管理我們可以建立一個主界面,根據(jù)不同的情況顯示不同的菜單,而連接管理我們可以使用一棵樹來進行管理,可以同時存在多個連接,這些連接下面的子節(jié)點就是該連接下面的數(shù)據(jù)庫。

          2.1 MySQL安裝目錄選擇界面

          當進入管理器時,我們就需要讓用戶去選擇MySQL的安裝目錄,原因就是因為我們需要MySQL的內(nèi)置命令,因此需要指定MySQL的安裝目錄。圖1是安裝目錄選擇界面。

          圖1 MySQL安裝目錄選擇界面

          讓用戶選擇MySQL安裝目錄十分簡單,只提供一個目錄選擇安裝以及顯示目錄路徑的JTextFeild,并且加入一個確定與取消按鈕。當用戶選擇了MySQL的安裝目錄,點擊了確定時,就顯示我們的主界面,這里需要注意的是,我們在實現(xiàn)的時候,需要判斷用戶所選擇的MySQL[安裝目錄是否正確,由于mysql與mysqldump等命令是存在于MySQL安裝目錄下的bin目錄的,因此判斷用戶所選擇的目錄是否正確,可以判斷在bin目錄下是否存在相應的命令,這些將在下面的章節(jié)中描述。MySQL安裝目錄在本章代碼中對應的是ConfigFrame類。

          2.2 主界面

          主界面提供各種功能的入口,可以讓用戶在該界面中使用或者進入各個功能,除了需要提供這些入口外,還需要提供一棵樹,更直觀的展示當前所使用的連接,以及該連接下面所有的數(shù)據(jù)庫。主界面如圖2所示。

          圖2 主界面

          主界面由一個工具欄,一棵樹以及一個JList組成,其中工具欄中包括的操作如下:

                添加連接:可以讓用戶添加一個連接。

                查看表:查看當前數(shù)據(jù)庫中所有的表。

                查看視圖:查看當前數(shù)據(jù)庫中所有的視圖。

                查看存儲過程(函數(shù)):查看數(shù)據(jù)庫中所有的存儲過程與函數(shù)。

                打開執(zhí)行SQL的界面:打開一個執(zhí)行SQL語句的界面。

          在主界面的左邊部分,提供一棵樹讓用戶十分直觀的看到連接的相關信息,這棵樹可以隱藏根節(jié)點,第一層節(jié)點就是連接,第二層節(jié)點就是該連接下所對應的所有的數(shù)據(jù)庫,每一個數(shù)據(jù)庫節(jié)點下面可以有三個子節(jié)點:表、視圖和存儲過程,當然,我們在平時使用其他管理工具的時候,還可以有觸發(fā)器等內(nèi)容,我們在本章的項目中不提供這些功能。

          這里需要注意的是,我們更換了樹的各個節(jié)點圖片,因此需要為JTree添加一個DefaultTreeCellRenderer來設置各個節(jié)點的圖片以及文字,當然,還需要將各個節(jié)點抽象成不同的對象,新建各個視圖對象的接口ViewObject,該接口將是所有視圖對象的父類,這些視圖對象包括樹的各個節(jié)點,主界面右邊列表所顯示的各個元素等。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectViewObject.java

          public interface ViewObject {

                   //返回顯示的圖片

                   Icon getIcon();

          }

          該接口只有一個getIcon方法,返回一個Icon對象,表示這些界面所對應的圖片,另外,樹上的各個節(jié)點對象,可以有兩種形式,第一種就是需要帶連接的節(jié)點,例如數(shù)據(jù)庫連接節(jié)點和數(shù)據(jù)庫節(jié)點,第二種就是不需要帶有連接的節(jié)點,因此我們可以將帶有連接的節(jié)點抽象成一個父類,讓連接節(jié)點和數(shù)據(jù)庫節(jié)點去繼承。另外,還需要提供一個connect的抽象方法,需要讓子類去實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeConnectionNode.java

          public abstract class ConnectionNode implements ViewObject {

                   //JDBC的Connection對象

                   protected Connection connection;

                   //連接方法,由子類去實現(xiàn)

                   public abstract Connection connect();

                   //省略setter和getter方法

          }

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeServerConnection.java

          public class ServerConnection extends ConnectionNode {

                   private final static String DRIVER = "com.mysql.jdbc.Driver";//MySQL驅動

                   private String connectionName; //MySQL驅動

                   private String username; //用戶名

                   private String password; //密碼

                   private String host; //連接ip

                   private String port; //連接端口

                   //省略setter和getter方法

                   //實現(xiàn)接口ViewObject的方法, 根據(jù)不同的連接狀態(tài)顯示不同的圖片

                   public Icon getIcon() {

                             if (super.connection == null) return ImageUtil.CONNECTION_CLOSE;

                             else return ImageUtil.CONNECTION_OPEN;

                   }

                   //重寫toString方法, 返回連接名稱

                   public String toString() {

                             return this.connectionName;

                   }

                   //實現(xiàn)父類的connect方法

                   public Connection connect() {

                   }

          }

          一個ServerConnection對象表示一個連接節(jié)點,一個連接節(jié)點當然需要包括一些連接的相關信息,包括連接名稱、MySQL用戶名、密碼、連接的IP與端口等信息。該對象實現(xiàn)了ViewObject的getIcon方法,判斷父類ConnectionNode的connection屬性是否為空來顯示不同的圖片,還需要重寫toString方法,返回連接的名稱。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

          public class Database extends ConnectionNode {

                   private String databaseName; //數(shù)據(jù)庫名字

                   private ServerConnection serverConnection; //數(shù)據(jù)庫所屬的服務器連接

                   //需要使用數(shù)據(jù)庫名稱與服務器連接對象構造

                   public Database(String databaseName, ServerConnection serverConnection) {

                             this.databaseName = databaseName;

                             this.serverConnection = serverConnection;

                   }

                   //實現(xiàn)接口的方法, 判斷該數(shù)據(jù)庫是否連接, 再返回不同的圖片

                   public Icon getIcon() {

                             if (this.connection == null) return ImageUtil.DATABASE_CLOSE;

                             return ImageUtil.DATABASE_OPEN;

                   }

                   //重寫toString方法

                   public String toString() {

                             return this.databaseName;

                   }

                   //實現(xiàn)父類的connect方法

                   public Connection connect() {

                   }

          }

          Database節(jié)點對象包括數(shù)據(jù)庫的名字,另外還需要一個ServerConnection對象,由于每個數(shù)據(jù)庫都是某個連接節(jié)點下面的子節(jié)點,因此需要記錄它的父節(jié)點,當然,并不是簡單的進行記錄,還可以讓它們共享一些不會經(jīng)常創(chuàng)建的實例,例如Connection。另外,需要注意的是,無論ServerConnection或者Database對象,都需要實現(xiàn)ViewObject的getIcon方法,當連接節(jié)點或者數(shù)據(jù)庫節(jié)點被打開時,都需要改變它們的圖片,而顯示何種圖片,由getIcon方法決定。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeTableNode.java

          public class TableNode implements ViewObject {

                   private Database database; //所屬的數(shù)據(jù)庫節(jié)點

                   //返回表的樹節(jié)點圖片

                   public Icon getIcon() {

                             return ImageUtil.TABLE_TREE_ICON;

                   }

                   //重寫toString方法

                   public String toString() {

                             return "表";

                   }

          }

          一個TableNode對象代表一個表的節(jié)點,需要提供一個Database屬性來表示這個對象是屬于哪個數(shù)據(jù)庫下面的子節(jié)點。如圖2所示,我們可以看樹中每個數(shù)據(jù)庫節(jié)點的表節(jié)點都是一致的(每個數(shù)據(jù)庫里面都有表),可以將這個表節(jié)點理解成是導航欄的某一組成部分,當用戶點擊了這個節(jié)點后,就可以在右邊的列表中顯示對應數(shù)據(jù)庫的表。

          與TableNode一樣,另外再次創(chuàng)建兩個對象:ViewNode和ProcedureNode,分別代表數(shù)據(jù)庫節(jié)點下面的視圖節(jié)點和存儲過程節(jié)點,實現(xiàn)方法與TableNode類似。下面為樹節(jié)點添加一個DefaultTreeCellRenderer類,讓其得到這些節(jié)點對象,并設置相應的文字和圖片。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui reeTreeCellRenderer.java

          public class TreeCellRenderer extends DefaultTreeCellRenderer {

                   public Component getTreeCellRendererComponent(JTree tree, Object value,

                                      boolean sel, boolean expanded, boolean leaf, int row,

                                      boolean hasFocus) {

                             DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;

                             //獲得每個節(jié)點的ViewObject

                             ViewObject obj = (ViewObject)node.getUserObject();

                             if (obj == null) return this;

                             this.setText(obj.toString());//設置文字

                             this.setIcon(obj.getIcon());//設置圖片

                             if (sel) this.setForeground(Color.blue); //判斷是否選來設置字體顏色

                             else this.setForeground(getTextNonSelectionColor());

                             return this;

                   }

          }

          在節(jié)點處理類TreeCellRenderer類,得到每個節(jié)點的ViewObject后,就可以為節(jié)點設置文字和圖片,我們的ViewObject接口提供了getIcon方法,所以我們就可以在節(jié)點處理類中得到每個節(jié)點所對應的圖片與文字(從toString方法獲得)。

          樹的相關處理就完成了,主界面的右邊是一個列表,對應的是一個JList對象,JList里面的每一個元素,都是ViewObject的實現(xiàn)類,只需要實現(xiàn)getIcon方法與重寫toString方法即可。每個列表的元素對象都可以將它們的name屬性抽象到一個父類中,各個對象去繼承它即可,在本例中,我們所涉及有三種數(shù)據(jù)類型:表、視圖和存儲過程(函數(shù)),我們需要建立三個對象,分別代表這三種數(shù)據(jù)類型。

          在本章的代碼中,我們創(chuàng)建了TableData、ViewData和ProcedureData三個類分別代表表數(shù)據(jù)、視圖數(shù)據(jù)和存儲過程數(shù)據(jù),這三個對象都需要實現(xiàn)ViewObject接口,具體的實現(xiàn)與三個節(jié)點的實現(xiàn)類似,都需要實現(xiàn)getIcon方法并重寫toString。表、視圖和存儲過程都是某一個數(shù)據(jù)庫下面的元素,因此這三個數(shù)據(jù)對象都需要保存一個Database屬性,表示該數(shù)據(jù)所屬于的數(shù)據(jù)庫。與樹一樣,還需要提供一個元素處理類,來指定顯示的數(shù)據(jù)圖片。

          代碼清單:codemysql-managersrcorgcrazyitmysqluilistListCellRenderer.java

          public class ListCellRenderer extends DefaultListCellRenderer {

                   public Component getListCellRendererComponent(JList list, Object value,

                                      int index, boolean isSelected, boolean cellHasFocus) {

                             JLabel label = (JLabel)super.getListCellRendererComponent(list,

                                               value, index, isSelected, cellHasFocus);

                             ViewObject vd = (ViewObject)value; //得到ViewObject對象

                             label.setIcon(vd.getIcon());//設置圖片

                             label.setToolTipText(vd.toString());

                             //設置選中時的字體顏色

                             if (isSelected) {

                                      setBackground(Color.blue);

                                      setForeground(Color.white);

                             }

                             return this;

                   }

          }

          到這里,主界面的各個對象都創(chuàng)建好了,本章中對應的主界面對象是MainFrame類,可以在該類中創(chuàng)建對應的樹與列表。這里需要注意的是,當創(chuàng)建列表(JList)的時候,可以將JList設置為橫向滾動,調(diào)用以下代碼即可實現(xiàn):

          dataList.setLayoutOrientation(JList.VERTICAL_WRAP); //dataList是界面中的JList對象

          創(chuàng)建主界面后,我們可以在創(chuàng)建樹與創(chuàng)建列表的時候加入一些模擬數(shù)據(jù)來查看效果,具體的效果如圖3所示:

          圖3 主界面效果

          2.3 數(shù)據(jù)顯示界面

          在整個管理器中,我們需要一個數(shù)據(jù)顯示的界面,而且只有一個。打開數(shù)據(jù)顯示界面的途徑有兩種,一種是雙擊一個表查看數(shù)據(jù)的時候,另外一種就是執(zhí)行SQL的時候(執(zhí)行查詢的SQL),就會打開數(shù)據(jù)顯示界面,將用戶感興趣的數(shù)據(jù)顯示出來。由于一般會存在打開多個表或者多次執(zhí)行SQL的情況,因此我們在編寫打開數(shù)據(jù)顯示界面的代碼的時候,每次都需要去創(chuàng)建這個界面對象的實例。在本章中,界面顯示對象對應的類是DataFrame,數(shù)據(jù)顯示界面如圖4所示。

          圖4 數(shù)據(jù)顯示界面

          界面比較簡單,一個工具條加一個表格即可,工具條中包括的功能有:

                刷新:刷新當前界面的數(shù)據(jù)。

                降序:當用戶選擇了某一列并點擊該圖標的時候,就對該列所對應的字段進行降序排序。

                升序:操作與降序一樣,但是對所選字段進行升序排序。

          在這個界面中,需要注意的是,這個列表對應的JTable對象并不像其他JTable一樣,擁有固定的列,由于我們不可能知道用戶將要打開的表有多少列,因此只能在用戶打開表的時候,得到該表的信息再動態(tài)的生成列與數(shù)據(jù)。除了這里之外,我們還需要為這個JTable對象進行一些額外的處理,例如我們需要讓這個JTable對象可以整列選擇,就需要自己編寫一個類去繼承JTable。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableDataTable.java

                   //當點擊表頭時, 表示當前所選擇的列

                   private int selectColumn = -1;

                   public DataTable(DefaultTableModel model) {

                             //為表頭添加鼠標事件監(jiān)聽器

                             header.addMouseListener(new MouseAdapter() {

                                      public void mouseClicked(MouseEvent e) {

                                               header.getTable().clearSelection();

                                               int tableColumn = header.columnAtPoint(e.getPoint());

                                               selectColumn = tableColumn;

                                      }

                             });

                             //為JTable添加鼠標監(jiān)聽器

                             this.addMouseListener(new MouseAdapter() {

                                      public void mouseClicked(MouseEvent e) {

                                               selectColumn = -1;

                                               updateUI();

                                      }

                             });

                   }

          注意以上代碼中的類屬性selectColumn,當我們用鼠標點擊了表頭的時候,就將該值設為當前選擇的列的索引,當在JTable的其他地方點擊了鼠標時,就設置該值為-1,表示沒有選擇表頭。那么我們就需要重寫JTable的isCellSelected方法,如果selectColumn不是-1,那么就需要將用戶所選擇的列整列設為選中狀態(tài),以下是isCellSelected方法的實現(xiàn):

                   //判斷一個單元格是否被選中, 重寫JTable的方法

                   public boolean isCellSelected(int row, int column) {

                             if (this.selectColumn == column) return true; //如果列數(shù)與當前選擇的列相同,返回true

                             return super.isCellSelected(row, column);

                   }

          另外,我們還需要提供一個返回selecColumn值的public的方法。做完這些后,可以點擊一列,看到效果如圖5所示。

          圖5 數(shù)據(jù)顯示界面選擇整列

          2.4 創(chuàng)建連接界面

          連接是整個工具的最基礎部分,沒有連接,其他任何操作都不能進行,因此使用這個MySQL管理工具,就需要提供一個新增連接的界面,讓用戶去創(chuàng)建各個連接,界面如圖6所示。

          圖6 新建連接界面

          圖6中新建連接的界面比較簡單,普通的一個表單,界面中包括的元素如下:

                連接名稱:該名稱在管理器的樹中顯示,并且該名稱不可以重復。

                連接IP:需要連接到的MySQL服務器IP。

                端口:MySQL的端口,默認為3306。

                用戶名:連接MySQL的用戶名,例如root。

                密碼:連接MySQL的密碼。

                測試連接:測試輸入的信息是否可以連接到MySQL服務器中,當然,如果測試不能連接,也可以添加這個連接。

                確定和取消:點擊確定添加連接并關閉該窗口,點擊取消不保存連接并關閉窗口。

          2.5 創(chuàng)建表界面

          當用戶需要創(chuàng)建一個表的時候,就需要提供一個界面讓用戶去輸入表的各種數(shù)據(jù),包括字段名稱、類型、是否允許空和主鍵等信息。創(chuàng)建表界面是本章中最為復雜的界面,用戶可以隨意的在表中進行操作,最后執(zhí)行保存,表界面如圖7所示。

          圖7 創(chuàng)建表界面

          界面如圖7所示,該界面較為復雜,分成上下兩個表格,上面的表格主要處理表的字段信息,包括字段名、類型、是否允許空和主鍵,在該表格下面,有一個輸入默認值的文本框,并提供一個表示字段是否自動增長的多選框。當我們在表格中選中某行數(shù)據(jù)(字段)的時候,默認值就需要發(fā)生相應的改變,自動增長的多選框也要隨著改變。在本章中表界面對應的是TableFrame類。

          字段表格需要進行特別處理的是允許空和主鍵的單元格,這兩個單元格都需要使用圖片來顯示。我們編寫一個FieldTable類來表示字段表格,并為這個FieldTable提供一個DefaultTableCellRenderer的子類來對單元格進行處理。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTableIconCellRenderer.java

          public class FieldTableIconCellRenderer extends DefaultTableCellRenderer {

                   public Component getTableCellRendererComponent(JTable table, Object value,

                                      boolean isSelected, boolean hasFocus, int row, int column) {

                             //判斷單元格的值類型,分別調(diào)用setIcon與setText方法

                             if (value instanceof Icon) this.setIcon((Icon)value);

                             else this.setText((String)value);

                             this.setHorizontalAlignment(CENTER);

                             return this;

                   }

          }

          FieldTableIconCellRenderer的實現(xiàn)十分簡單,只是判斷單格的值再進行處理。在FieldTable使用以下代碼即可實現(xiàn)顯示圖片。

                             this.getColumn(ALLOW_NULL).setCellRenderer(this.cellRenderer);

                             this.getColumn(PRIMARY_KEY).setCellRenderer(this.cellRenderer);

          以上代碼先得到允許空和主鍵的列后再設置單元格處理類。重新運行程序時,就可以看到效果如圖7所示,但是否需要對FieldTable加入鼠標事件處理,當點擊了允許空和主鍵的列單元格時,就需要改變它們圖片。為FieldTable加入鼠標監(jiān)聽器。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTable.java

                   //鼠標在JTable中點擊的時候觸發(fā)該方法

                   private void selectCell() {

                             int column = this.getSelectedColumn();

                             int row = this.getSelectedRow();

                             if (column == -1 || row == -1) return;

                             //修改圖片列

                             selectAllowNullColumn(row, column);

                             selectPrimaryKeyColumn(row, column);

                   }

                   //點擊的單元格位于允許空列

                   private void selectAllowNullColumn(int row, int column) {

                             //得到需要更改圖片的列(允許空列)

                             TableColumn tc = this.getColumn(ALLOW_NULL);

                             if (tc.getModelIndex() == column) {

                                      Icon currentIcon = (Icon)this.getValueAt(row, column);

                                      //根據(jù)當前選中的圖片來更改允許空的圖片

                                      if (ImageUtil.CHECKED_ICON.equals(currentIcon)) {

                                               this.setValueAt(ImageUtil.UN_CHECKED_ICON, row, column);

                                      } else {

                                               this.setValueAt(ImageUtil.CHECKED_ICON, row, column);

                                      }

                             }

                   }

                   //如果鼠標點擊的列是"主鍵列",去掉或者加上圖標

                   private void selectPrimaryKeyColumn(int row, int column) {

                             //得到需要更改圖片的列(主鍵列)

                             TableColumn tc = this.getColumn(PRIMARY_KEY);

                             if (tc.getModelIndex() == column) {

                                      Object obj = this.getValueAt(row, column);

                                      if (ImageUtil.PRIMARY_KEY_BLANK.equals(obj)) {

                                               this.setValueAt(ImageUtil.PRIMARY_KEY, row, column);

                                      } else {

                                               this.setValueAt(ImageUtil.PRIMARY_KEY_BLANK, row, column);

                                      }

                             }

                   }

          只需要在創(chuàng)建FieldTableIconCellRenderer的時候為表格加入鼠標監(jiān)聽器,該監(jiān)聽器調(diào)用以上代碼的selectCell方法即可,selectCell方法再去調(diào)用點擊允許空和主鍵單元格的方法,即以上的selectAllowNullColumn和selectPrimaryKeyColumn方法,這兩個方法中判斷用戶所選擇的列,是否為需要進行圖片處理的列(允許空和主鍵),再對單元格的值(圖片)進行修改,就可以達到點擊單元格就顯示不同圖片的效果。另外,當我們點擊了某行數(shù)據(jù)(字段)的時候,還需要處理默認值與自動增長,我們在下面章節(jié)將會實現(xiàn)。

          實現(xiàn)了字段列表后,還需要注意的是該列表下面的三個按鈕,分別是新字段、插入字段和刪除字段,新字段與插入字段的區(qū)別是,新字段在列表的最后加入一行數(shù)據(jù),插入字段在用戶所選擇的行的前面插入一行數(shù)據(jù)。

          TableFrame下面的外鍵列表與字段列表不同的是,外鍵列表不需要進行圖片處理,但是每個單元格都需要使用下拉框來代替普通的文字。與字段列表一樣,新建一個ForeignTable的類來表示一個外鍵列表,外鍵列表有5列,而且每一列中的每個單元格都是下拉框,因此我們需要在ForeignTable中創(chuàng)建5個下拉框(JComboBox)以及5個單元格編輯器對象。

          5個單元格編輯器對象,以下是ForeignTable的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableForeignTable.java

                   private DefaultCellEditor fieldNameEditor; //字段名稱編輯器對象

                   private DefaultCellEditor referenceTableEditor; //約束表

                   private DefaultCellEditor referenceFieldEditor; //約束字段

                   private DefaultCellEditor onDeleteEditor; //級聯(lián)刪除

                   private DefaultCellEditor onUpdateEditor; //級聯(lián)更新

          那么在創(chuàng)建這些單元格編輯器對象的時候,就分別以各個下拉框的對象作為構造參數(shù):

                   this.fieldNameEditor = new DefaultCellEditor(this.fieldNameComboBox);

          接下來,得到相應的列,再設置編輯器對象即可:

                   this.getColumn(FIELD_NAME).setCellEditor(this.fieldNameEditor);

          做完這些工作后,外鍵列表中所有的單元格都變成可以使下拉來設定值,我們在開發(fā)界面的時候,由于缺乏真實的數(shù)據(jù),因此我們可以提供一些模擬的數(shù)據(jù)來實現(xiàn)效果,到需要實現(xiàn)的時候,就可以替換上真實的數(shù)據(jù)。新增表與修改表的界面可以共用一個界面,但是同時需要做新增與修改操作的時候,就需要做多一些額外的判斷,本章中新增表與修改表為同一個界面(TableFrame)。

          2.6 視圖界面

          當用戶需要編寫一個視圖的時候,我們可以提供一個視圖界面。視圖界面實現(xiàn)十分簡單,只有一個JTextArea即可,并附帶有保存操作。這里需要注意的是,用戶點擊保存的時候,需要將視圖通過SQL的CREATE VIEW來創(chuàng)建,那么用戶查看視圖的時候,與查看表一樣,都是需要打開數(shù)據(jù)瀏覽界面。圖8是視圖界面。

          圖8 視圖界面

          在本章中,創(chuàng)建表的界面一樣,無論新增視圖或者修改視圖,都使用相同的一個界面,對應的是ViewFrame。

          2.7 存儲過程界面

          用戶需要新建一個存儲過程或者函數(shù)的時候,可以提供一個新建存儲過程界面讓用戶去操作。存儲界面在本章中對應的類是ProcedureFrame。存儲過程界面如圖9所示。

          圖9 存儲過程界面

          界面元素說明:

                輸入方法體的JTextArea:用戶可以在此輸入存儲過程或者函數(shù)的方法體。

                參數(shù)JTextField:輸入存儲過程或者函數(shù)的參數(shù)。

                返回值JTextField:可以輸入函數(shù)的返回值,因為函數(shù)才有返回值。如果選擇的類型為存儲過程,則該JTextField不可用。

                類型下拉框:可以選擇編寫的類型,是存儲過程還是函數(shù)。

          2.8 查詢界面

          當用戶需要執(zhí)行一些SQL的時候,可以提供一個查詢界面讓用戶去輸入,該界面提供執(zhí)行SQL與保存SQL的功能,執(zhí)行SQL的時候,如果是普通的INSERT、UPDATE或者其他無需瀏覽數(shù)據(jù)的SQL語句,則可以直接操作。如果執(zhí)行的是查詢、調(diào)用存儲過程或者函數(shù)的語句,那么就需要將結果顯示到數(shù)據(jù)界面,即2.3的界面。本章對應的查詢界面類是QueryFrame,查詢界面如圖10所示。

          圖10 查詢界面

          2.9 樹節(jié)點右鍵菜單

          在主界面的連接樹中,當我們點擊了樹的某個節(jié)點的時候,可以提供一些右鍵菜單來執(zhí)行一些相關的操作,例如點擊了連接節(jié)點,就可以提供關閉連接、刪除連接等右鍵菜單,如果點擊了數(shù)據(jù)庫節(jié)點,就可以提供關閉數(shù)據(jù)庫或者刪除數(shù)據(jù)庫等右鍵菜單。

          點擊連接節(jié)點的右鍵菜單如圖11所示。

          圖11 連接節(jié)點菜單

          點擊數(shù)據(jù)庫節(jié)點的右鍵菜單如圖12所示。

          圖12 數(shù)據(jù)庫節(jié)點右鍵菜單

          由于我們對連接節(jié)點或者數(shù)據(jù)庫節(jié)點進行選擇的時候,就可以打開連接或者數(shù)據(jù)庫,因此并不需要提供打開的菜單,本章中使用JPopupMenu來實現(xiàn)鼠標右鍵菜單,MainFrame中提供一個JPopupMenu對象來存放各個菜單當點擊了連接節(jié)點的時候JPopupMenu刪除所有的子菜單,再加入連接節(jié)點的菜單(JMenuItem),數(shù)據(jù)庫節(jié)點的實現(xiàn)方式與之相同。

          2.10 數(shù)據(jù)列表右鍵菜單

          主界面中除了連接樹外,還有一個數(shù)據(jù)列表,當用戶在樹中點擊了表節(jié)點、視圖節(jié)點或者存儲過程節(jié)點的時候,數(shù)據(jù)列表中就顯示不同的數(shù)據(jù),我們可以根據(jù)當前所顯示的數(shù)據(jù)來創(chuàng)建不同的鼠標右鍵菜單。圖13是數(shù)據(jù)列表顯示表數(shù)據(jù)的時候的右鍵菜單。

          圖13 表數(shù)據(jù)菜單

          表數(shù)據(jù)鼠標右鍵菜單說明:

                新建表:打開創(chuàng)建表的界面,即2.5中的界面。

                編輯表:修改一個表,與新建表使用同一個界面。

                刪除表:刪除列表中選擇數(shù)據(jù)。

                導出表:將一個表的數(shù)據(jù)導出。

          視圖數(shù)據(jù)的鼠標右鍵菜單如圖14所示。

          圖14 視圖數(shù)據(jù)菜單

          視圖數(shù)據(jù)鼠標右鍵菜單說明:

                新建視圖:打開2.6中的視圖界面,用于創(chuàng)建視圖。

                編輯視圖:修改所選擇的視圖,與新建視圖使用同一個界面。

                刪除視圖:刪除所選擇的視圖。

          存儲過程鼠標右鍵菜單如圖15所示。

          圖15 存儲過程數(shù)據(jù)菜單

          存儲過程鼠標右鍵菜單說明:

                新建存儲過程:打開2.7中的存儲過程界面,創(chuàng)建存儲過程。

                編輯存儲過程:修改選擇的存儲過程,與新建存儲過程使用相同的界面。

                刪除存儲過程:刪除所選擇的存儲過程或者函數(shù)。

          以上為三種數(shù)據(jù)的右鍵菜單,實現(xiàn)方式與樹節(jié)點的右鍵菜單一樣,當界面的數(shù)據(jù)發(fā)生改變時,就相應的去刪除JPopupMenu所有的子菜單,再添加相應的菜單(JMenuItem)即可。

          以上的菜單均在主界面(MainFrame)中創(chuàng)建,程序并不知道當前顯示的是哪種數(shù)據(jù),因此我們需要在MainFrame中提供一個ViewObject的類來標識當前顯示的類型,ViewObject是所有界面元素都需要實現(xiàn)的接口,表數(shù)據(jù)是TableData類,視圖數(shù)據(jù)是ViewData類,存儲過程數(shù)據(jù)是ProcedureData類,詳細請看2.2中的各個界面對象。當用戶點擊了工具欄或者樹上的某個節(jié)點時,就相應的改變MainFrame中的ViewObject即可。

          到此,管理器的所有界面都創(chuàng)建完畢,接下來就可以實現(xiàn)相關的功能。

          3 實現(xiàn)MySQL安裝目錄選擇功能

          實現(xiàn)MySQL安裝目錄選擇功能,我們使用2.1的界面。當用戶進入管理器的時候,就讓用戶選擇本地的MySQL安裝目錄,由于我們需要使用MySQL的一些內(nèi)置命令,因此選擇MySQL的安裝目錄是一個必要的操作,得到MySQL安裝目錄后,我們就可以找到bin目錄下面的命令。因此用戶選擇了安裝目錄后,我們的程序就需要對所選擇目錄進行驗證,判斷能否找到bin目錄。

          3.1 實現(xiàn)目錄選擇

          選擇目錄實現(xiàn)十分簡單,只需要提供一個文件選擇器即可,而且這個文件選擇器只可以選擇目錄,當用戶選擇了對應的目錄后,就可以將其選擇的目錄顯示到2.1界面的JTextField中。文件選擇器的代碼如下。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConfigFrame.java

                   private JTextField field;

                   public FileChooser(JTextField field) {

                             this.field = field;

                             //設置只可以選擇目錄

                             this.setFileSelectionMode(FileChooser.DIRECTORIES_ONLY);

                   }

                   //重寫JFileChooser的方法

                   public void approveSelection() {

                             //設置JTextField的值

                             this.field.setText(this.getSelectedFile().getAbsolutePath());

                             super.approveSelection();

                   }

                  

          用戶選擇目錄后,就將其所選的目錄的絕對路徑顯示到JTextField中,當點擊確定的時候,就可以進行判斷,以下代碼為點擊確定所執(zhí)行的代碼。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConfigFrame.java

                             //取得用戶輸入值

                             String mysqlHome = this.mysqlHomeField.getText();

                             //尋找用戶選擇的目錄,判斷是否可以找到MySQL安裝目錄下的bin目錄

                             File file = new File(mysqlHome + MySQLUtil.MYSQL_HOME_BIN);

                             //找不到MySQL的安裝目錄,提示

                             if (!file.exists()) {

                                      showMessage("請選擇正確MySQL安裝目錄", "錯誤");

                                      return;

                             }

          以上代碼的黑體部分,需要去判斷MySQL安裝目錄下的bin目錄是否存在,如果沒有存該目錄,則表示用戶所選擇的目錄是錯誤的,彈出提示并返回。如果用戶選擇的目錄是正確的話,就需要去讀取管理器的配置文件。

          3.2 讀取和保存安裝目錄路徑

          用戶選擇了MySQL的安裝目錄后,我們需要將目錄的絕對路徑保存到一份配置文件中,這樣做的話,就可以不必每一次都去進行目錄選擇。提供一份mysql.properties的配置文件,以下為該配置文件的讀取代碼。

          代碼清單:codemysql-managersrcorgcrazyitmysqlutilFileUtil.java

                   //返回配置文件的MYSQL_HOME配置

                   public static String getMySQLHome() {

                             File configFile = new File(MYSQL_PROPERTIES_FILE);

                             Properties props = getProperties(configFile);

                             return props.getProperty(MYSQL_HOME);

                   }

          以上代碼中的MYSQL_PROPERTIES_FILE就是mysql.properties配置文件的相對路徑,找到該文件后,就讀取它的mysql.home屬性。那么用戶在進入MySQL安裝目錄選擇界面的時候,就可以調(diào)用以上的方法去獲得MySQL安裝目錄的值。

          接下來實現(xiàn)保存安裝目錄的功能,在這之前,新建一個GlobalContext的類,用于保存管理器全局的一些信息,例如這里的mysql.home屬性。以下代碼實現(xiàn)保存配置的功能。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConfigFrame.java

                             //省略其他代碼...

                             //如果配置文件的值與用戶輸入的值不相等,則重新寫入配置文件中

                             if (!mysqlHome.equals(FileUtil.getMySQLHome())) {

                                      FileUtil.saveMysqlHome(this.mysqlHomeField.getText());

                             }

                             GlobalContext ctx = new GlobalContext(mysqlHome);

                             this.mainFrame = new MainFrame(ctx);

                             this.mainFrame.setVisible(true);

                             this.setVisible(false);

          注意以上代碼的判斷,如果用戶前一次所選擇的MySQL安裝目錄與這一次所選擇的目錄不一致,則需要重新將新的目錄信息保存到mysql.properties文件中。這些做的話,就不需要每一次進入系統(tǒng)都去修改配置文件。

          3.3 讀取連接信息

          在得到MySQL安裝目錄,進入主界面時,還需要得到用戶所有的連接信息,這些信息用來初始化主界面左邊的樹,管理器是針對MySQL數(shù)據(jù)庫的,但是這些連接信息可以不記錄到數(shù)據(jù)庫,與保存MySQL安裝目錄一樣,可以提供一些properties文件來保存,每一個連接作為一份properties文件。保存連接的信息我們在下面的章節(jié)中實現(xiàn),這里主要實現(xiàn)讀取的實現(xiàn)。

          新建一個PropertiesHandler的接口,專門用于處理連接屬性文件。該接口提供一個讀取數(shù)據(jù)庫連接配置文件的方法,并返回ServerConnection集合,ServerConnection代表一個連接節(jié)點,并保存有一些數(shù)據(jù)庫連接的信息,詳細請看2.2中的ServerConnection類。以下代碼讀取一份properties,并返回一個Properties對象。

          代碼清單:codemysql-managersrcorgcrazyitmysqlutilFileUtil.java

                   //根據(jù)文件得到對應的properties文件

                   public static Properties getProperties(File propertyFile) throws IOException {

                             Properties prop = new Properties();

                             FileInputStream fis = new FileInputStream(propertyFile);

                             prop.load(fis);

                             fis.close();

                             return prop;

                   }

          那么在PropertiesHandler實現(xiàn)類中,就可以讀取相應目錄下的所有properties文件。

          代碼清單:codemysql-managersrcorgcrazyitmysqlsystemPropertiesHandlerImpl.java

                   //得到所有的連接信息

                   public List<ServerConnection> getServerConnections() {

                             File[] propertyFiles = getPropertyFiles();

                             List<ServerConnection> result = new ArrayList<ServerConnection>();

                             for (File file : propertyFiles) {

                                      ServerConnection conn = createServerConnection(file);

                                      result.add(conn);

                             }

                             return result;

                   }

                   //將一份properties文件封裝成ServerConnection對象

                   private ServerConnection createServerConnection(File file) {

                             Properties prop = FileUtil.getProperties(file);

                             ServerConnection conn = new ServerConnection(FileUtil.getFileName(file),

                                               prop.getProperty(FileUtil.USERNAME),

                                               prop.getProperty(FileUtil.PASSWORD),

                                               prop.getProperty(FileUtil.HOST),

                                               prop.getProperty(FileUtil.PORT));

                             return conn;

                   }

          得到所有的連接信息后,先不需要初始化樹,需要將這些信息存放到一個對象中,因為在下面的實現(xiàn)中,這些類或者連接信息需要經(jīng)常使用到。在3.2中提供了一個GlobalContext的類來表示管理器的上下文,可以將這些連接信息放到該類中。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectGlobalContext.java

                   //存放所有服務器連接的集合

                   private Map<String, ServerConnection> connections = new HashMap<String, ServerConnection>();

                   //添加一個連接到Map中

                   public void addConnection(ServerConnection connection) {

                             this.connections.put(connection.getConnectionName(), connection);

                   }

          在GlobalContext中建立一個Map來保存這些連接信息,并提供add方法,由于這個Map是使用連接的名稱作為key的,所以就決定了在管理器中不允許出現(xiàn)重名的連接。那么在用戶選擇MySQL安裝目錄,點擊確定后,就可以將連接加入到GlobalContext中,用戶點擊確定按鈕執(zhí)行的部分代碼。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConfigFrame.java

                             //讀取全部的服務器連接配置

                             List<ServerConnection> conns = ctx.getPropertiesHandler().getServerConnections();

                             for (ServerConnection conn : conns) ctx.addConnection(conn);

          到此,MySQL安裝目錄的功能已經(jīng)實現(xiàn),得到用戶的各個連接信息后,就可以根據(jù)這些連接實現(xiàn)創(chuàng)建樹的功能。

          4 連接管理

          進入主界面后,我們需要將各個連接信息創(chuàng)建一棵樹,用戶往后的各個操作,都與這些棵樹息息相關。樹的第一層節(jié)點是管理器中的各個連接,只需要得到各個連接后,以這些連接對象創(chuàng)建第一層節(jié)點即可。本小節(jié)將實現(xiàn)連接相關的功能,這些功能包括創(chuàng)建連接節(jié)點、打開連接、刪除連接等。

          4.1 創(chuàng)建連接節(jié)點

          進入主界面時,我們已經(jīng)可以得到GlobalContext對象,各個連接信息都保存在該對象中,因此可以根據(jù)這些連接信息來創(chuàng)建樹。在創(chuàng)建樹的時候,需要注意的是,我們只需要根據(jù)這些連接信息來創(chuàng)建第一層節(jié)點,而不需要再去創(chuàng)建下面的幾層節(jié)點,當用戶點擊第一層節(jié)點(連接節(jié)點)的時候,再去訪問該連接下面的數(shù)據(jù)庫信息,正常得到這些數(shù)據(jù)庫后,再創(chuàng)建數(shù)據(jù)庫節(jié)點。

          MainFrame中創(chuàng)建連接節(jié)點的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //創(chuàng)建樹中服務器連接的節(jié)點

                   private void createNodes(DefaultMutableTreeNode root) {

                             Map<String, ServerConnection> conns = this.ctx.getConnections();

                             for (String key : conns.keySet()) {

                                      ServerConnection conn = conns.get(key);

                                      //創(chuàng)建連接節(jié)點

                                      DefaultMutableTreeNode conntionNode = new DefaultMutableTreeNode(conn);

                                      root.add(conntionNode);

                             }

                   }

          MainFrame中創(chuàng)建樹的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //創(chuàng)建樹

                   private void createTree() {

                             DefaultMutableTreeNode root = new DefaultMutableTreeNode(new RootNode());

                             //創(chuàng)建連接節(jié)點

                             createNodes(root);

                             this.treeModel = new DefaultTreeModel(root);

                             //構造樹

                             JTree tree = new JTree(this.treeModel);

                             //設置節(jié)點處理類

                             TreeCellRenderer cr = new TreeCellRenderer();

                             tree.setCellRenderer(cr);

                             //設置監(jiān)聽器類

                             tree.addMouseListener(new TreeListener(this));

                             tree.setRootVisible(false);

                             //添加右鍵菜單

                             tree.add(this.treeMenu);

                             this.tree = tree;

                   }

          以上代碼中的TreeCellRenderer為節(jié)點的處理類,具體的實現(xiàn)請看2.2中TreeCellRenderer類的實現(xiàn)。TreeListener是一個鼠標事件監(jiān)聽器,當用戶點擊了樹的連接節(jié)點后,需要建立連接,我們在下面的章節(jié)中實現(xiàn)。

          4.2 打開連接

          當用戶點擊連接節(jié)點后,就需要立即打開這個連接,我們使用了一個ServerConnection對象來保存連接的信息,并使用該對象來創(chuàng)建樹中的連接節(jié)點,打開連接的時候,就可以根據(jù)連接的信息去嘗試進行服務器連接,使用JDBC進行連接即可。如果成功進行連接,就馬上創(chuàng)建該連接節(jié)點的子節(jié)點(數(shù)據(jù)庫節(jié)點),如果不能成功連接,則彈出提示。ServerConnection繼承了ConnectionNode這個抽象類,ConnectionNode中保存了一個JDBC的Connection對象,ServerConnection中判斷是否連接的標準是判斷ConnectionNode的Connection對象是否為空,而且ServerConnection中需要實現(xiàn)父類(ConnectionNode)的connect方法,下面代碼是ServerConnection對ConnectionNode的connect方法的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeServerConnection.java

                   //實現(xiàn)父類的方法

                   public Connection connect() {

                             //Connection在本類中只有一個實例

                             if (super.connection != null) return super.connection;

                             Class.forName(DRIVER);

                             Connection conn = createConnection("");

                             super.connection = conn;

                             return super.connection;

                   }

                   //創(chuàng)建連接, 參數(shù)是數(shù)據(jù)庫名稱

                   public Connection createConnection(String database) throws Exception {

                             Class.forName(DRIVER);

                             Connection conn = DriverManager.getConnection(getConnectUrl() + database,

                                               this.username, this.password);

                             return conn;

                   }

          以上的代碼中先判斷ServerConnection的connection屬性是否為空,如果該屬性為空(沒有連接)則進行創(chuàng)建。注意createConnection方法,該方法聲明為public,可以讓外部去使用。下面實現(xiàn)樹的節(jié)點監(jiān)聽器,當節(jié)點被選中后,就可以執(zhí)行connect方法,但是需要注意的是,并不是每個節(jié)點都相同,只是連接節(jié)點被點擊的時候才去進行連接。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui reeTreeListener.java

                   public void mousePressed(MouseEvent e) {

                             if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {

                                      //左鍵點擊,查看樹的節(jié)點,調(diào)用MainFrame的打開節(jié)點方法

                                      this.mainFrame.viewTreeDatas();

                             }

                   }

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //點擊樹節(jié)點的操作

                   public void viewTreeDatas() {

                             //獲得選中的節(jié)點

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             if (selectNode == null) return;

                             //判斷點擊節(jié)點的類型

                             if (selectNode.getUserObject() instanceof ServerConnection) {

                                      clickServerNode(selectNode);//服務器連接節(jié)點

                             }

                   }

                   //點擊服務器節(jié)點

                   public void clickServerNode(DefaultMutableTreeNode selectNode) {

                             //暫時不實現(xiàn)

                   }

          連接節(jié)點被點擊后,就會執(zhí)行clickServerNode方法,該方法需要做的是先去驗證被選中的節(jié)點是否可以進行連接,再創(chuàng)建該連接節(jié)點的子節(jié)點(數(shù)據(jù)庫節(jié)點)。要創(chuàng)建數(shù)據(jù)庫節(jié)點,就要得到該連接下面所有的數(shù)據(jù)庫,執(zhí)行MySQL的一句show databases就可以得到所有的數(shù)據(jù)庫。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeServerConnection.java

                   //獲得一個服務器連接下面所有的數(shù)據(jù)庫

                   public List<Database> getDatabases() {

                             List<Database> result = new ArrayList<Database>();

                             try {

                                      //獲得一個連接下面所有的數(shù)據(jù)庫

                                      ResultSet rs = query("show databases");

                                      while (rs.next()) {

                                               String databaseName = rs.getString("Database");

                                               Database db = new Database(databaseName, this);

                                               result.add(db);

                                      }

                                      rs.close();

                                      return result;

                             } catch (Exception e) {

                                      return result;

                             }

                   }

                   //查詢并返回ResultSet對象

                   public ResultSet query(String sql) throws Exception {

                             Statement stmt = getStatement();

                             return stmt.executeQuery(sql);

                   }

          使用Statement執(zhí)行show databases就可以得到所有的數(shù)據(jù)庫ResultSet對象,這里需要注意的是,我們需要得到Statement對象,直接使用ConnectionNode的connection屬性去創(chuàng)建Statement對象即可,并不需要再去重新創(chuàng)建JDBC的Connection對象。查詢到數(shù)據(jù)庫的ResultSet對象后,就將Database列的值封裝成一個Database對象,加入到結果集中即可。在本章中,一個Database對象代表一個數(shù)據(jù)庫節(jié)點。那么現(xiàn)在就可以實現(xiàn)clickServerNode方法,點擊了連接節(jié)點后,就會執(zhí)行該方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //點擊服務器節(jié)點

                   public void clickServerNode(DefaultMutableTreeNode selectNode) {

                             ServerConnection server = (ServerConnection)selectNode.getUserObject();

                             //驗證是否可以進行連接

                             validateConnect(selectNode, server);

                             //創(chuàng)建服務器子節(jié)點

                             buildServerChild(server, selectNode);

                   }

                   //創(chuàng)建數(shù)據(jù)庫一層的節(jié)點(樹的第二層)

                   public void buildServerChild(ServerConnection server,

                                      DefaultMutableTreeNode conntionNode) {

                             //如果有子節(jié)點,則不再創(chuàng)建

                             if (conntionNode.getChildCount() != 0) return;

                             List<Database> databases = server.getDatabases();

                             //再創(chuàng)建連接節(jié)點下面的數(shù)據(jù)節(jié)點

                             for (Database database : databases) {

                                      DefaultMutableTreeNode databaseNode = new DefaultMutableTreeNode(database);

                                      //將數(shù)據(jù)庫節(jié)點加入到連接節(jié)點中

                                      this.treeModel.insertNodeInto(databaseNode, conntionNode, conntionNode.getChildCount());

                             }

                   }

                   //判斷連接是否出錯,適用于服務器節(jié)點和數(shù)據(jù)庫節(jié)點

                   private void validateConnect(DefaultMutableTreeNode selectNode, ConnectionNode node) {

                             //進行連接

                             node.connect();

                   }

          打開連接的功能已經(jīng)實現(xiàn),可以運行程序查看效果??偟膩碚f,打開一個連接需要做的是:驗證連接和創(chuàng)建數(shù)據(jù)庫節(jié)點。

          4.3 新建連接

          在2.4中,我們已經(jīng)提供了一個創(chuàng)建連接的界面,實現(xiàn)新建連接,只需要將用戶輸入的連接信息保存到一份properties文件中,再向樹中添加一個連接節(jié)點即可。我們?yōu)榻涌赑ropertiesHandler添加一個saveServerConnection的方法,PropertiesHandler是用于處理properties文件的接口,在3.3中已經(jīng)創(chuàng)建。

          PropertiesHandler實現(xiàn)類對saveServerConnection的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlsystemPropertiesHandlerImpl.java

                   public void saveServerConnection(ServerConnection conn) {

                             //得到配置文件名, 這些properties文件存放于connections目錄下

                             String configFileName = FileUtil.CONNECTIONS_FOLDER +

                                      conn.getConnectionName() + ".properties";

                             //創(chuàng)建properties文件

                             File connConfigFile = new File(configFileName);

                             //創(chuàng)建文件

                             FileUtil.createNewFile(connConfigFile);

                             Properties props = new Properties();

                             props.setProperty(FileUtil.HOST, conn.getHost());

                             props.setProperty(FileUtil.PORT, conn.getPort());

                             props.setProperty(FileUtil.USERNAME, conn.getUsername());

                             props.setProperty(FileUtil.PASSWORD, conn.getPassword());

                             //將屬性寫入配置文件

                             FileUtil.saveProperties(connConfigFile, props, "Connection " +

                                               conn.getConnectionName() + " config.");

                   }

          saveServerConnection實現(xiàn)簡單,只需要將ServerConnection對象中的各個屬性寫到properties文件中即可。那么在ConnectionFrame(新建連接界面)中,當用戶輸入各個信息點擊確定后,就可以對這些連接進行保存,以下為點擊確定執(zhí)行的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConnectionFrame.java

                   //保存連接

                   private void saveConnection() {

                             //得到用戶輸入的信息并返回一個ServerConnection對象

                             ServerConnection conn = getDataConnectionFromView();

                             //判斷連接名稱是否重復

                             if (this.ctx.getConnection(conn.getConnectionName()) != null) {

                                      showMessage("已經(jīng)存在相同名字的連接", "錯誤");

                                      return;

                             }

                             //直接保存, 不需要創(chuàng)建任何的連接, 添加到GlobalContext的連接Map中

                             this.ctx.addConnection(conn);

                             //保存到屬性文件

                             this.ctx.getPropertiesHandler().saveServerConnection(conn);

                             this.mainFrame.addConnection(conn);

                             this.setVisible(false);

                   }

          注意以上代碼的黑體部分,需要進連接的名字進行判斷,先去GlobalContext的連接Map中獲取ServerConnection對象,如果能得到,則表示已經(jīng)存在相同名字的連接。保存到屬性文件后,就調(diào)用MainFrame的addConnection方法,以下是addConnection方法的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //在添加連接界面添加了一個連接后執(zhí)行的方法, 向樹中添加一個連接

                   public void addConnection(ServerConnection sc) {

                             //得到要節(jié)點

                             DefaultMutableTreeNode root = (DefaultMutableTreeNode)this.treeModel.getRoot();

                             DefaultMutableTreeNode newChild = new DefaultMutableTreeNode(sc);

                             //向要節(jié)點添加連接節(jié)點

                             this.treeModel.insertNodeInto(newChild, root, root.getChildCount());

                             if (root.getChildCount() == 1) this.tree.updateUI();             

                   }

          addConnection方法直接使用DefaultTreeModel的insertNodeInto方法向樹添加一個ServerConnection節(jié)點。運行程序并進行添加一個連接,可以看到具體的效果。除了添加連接的功能外,界面中還有一個測試連接的功能,在添加連接前,可以先測試一下服務器是否可以連接。以下是點擊測試連接按鈕觸發(fā)的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiConnectionFrame.java

                   //測試連接

                   private void checkConnection() {

                             //從界面中得到連接信息

                             ServerConnection conn = getDataConnectionFromView();

                             try {

                                      conn.connect();

                                      showMessage("成功連接", "成功");

                             } catch (Exception e) {

                                      showMessage(e.getMessage(), "警告");

                             }

                   }

          與打開連接一樣,都是使用ServerConnection的connect方法進行連接,再捕獲異常。保存的時候,我們會再去從界面獲取一個ServerConnection對象,因此測試連接的ServerConnection對象與保存時候的ServerConnection是兩個對象。

          4.4 刪除連接

          用戶選擇了一個連接需要刪除的時候,就需要提供一個刪除連接的功能,刪除連接的功能我們在右鍵菜單中提供,當用戶選擇了某個連接節(jié)點的時候,就彈出該菜單,該菜單已經(jīng)在2.9中實現(xiàn),下面實現(xiàn)刪除連接的功能。首先我們需要明白的是,刪除一個連接,就是從管理器中徹底刪除這個連接信息,再從樹中刪除這個連接節(jié)點,最后還需要從GlobalContext的連接Map中刪除該連接。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //刪除一個連接

                   private void removeConnection() {

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             ServerConnection conn = (ServerConnection)selectNode.getUserObject();

                             //從上下文件中刪除

                             this.ctx.removeConnection(conn);

                             //從樹節(jié)點中刪除

                             this.treeModel.removeNodeFromParent(selectNode);

                   }

          當用戶選擇了某個連接節(jié)點的時候,選擇右鍵菜單中的刪除連接,就會觸發(fā)上面的removeConnection方法,只需要為菜單對象添加ActionListener即可。先調(diào)用GlobalContext的刪除連接方法將ServerConnection從全局上下文中刪除,再使用DefaultTreeModel將該節(jié)點從樹上刪除。以下是GlobalContext中刪除ServerConnection的方法(以上代碼的黑體部分)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectGlobalContext.java

                   //從Map中刪除一個連接

                   public void removeConnection(ServerConnection connection) {

                             //刪除該連接的配置文件

                             File configFile = new File(FileUtil.CONNECTIONS_FOLDER +

                                               connection.getConnectionName() + ".properties");

                             configFile.delete();

                             this.connections.remove(connection.getConnectionName());

                   }

          GlobalContext中的removeConnection方法,先刪除properties文件,再從Map中刪除該ServerConnection對象。

          4.5 關閉連接

          關閉一個服務器的連接,需要將ServerConnection對象的connection屬性設置為true,connection屬性保存在ServerConnection的父類ConnectionNode中,設置該屬性為null后,連接節(jié)點的圖標就自然會變成關閉的圖標,因為ServerConnection中實現(xiàn)了ViewObject的getIcon方法。另外,還需要幫ServerConnection節(jié)點刪除它的全部子節(jié)點。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //刪除一個節(jié)點的所有子節(jié)點

                   private void removeNodeChildren(DefaultMutableTreeNode node) {

                             //獲取節(jié)點數(shù)量

                             int childCount = this.treeModel.getChildCount(node);

                             for (int i = 0; i < childCount; i++) {

                                      //從最后一個開始刪除

                                      this.treeModel.removeNodeFromParent((DefaultMutableTreeNode)node.getLastChild());

                             }

                   }

                   //關閉服務器連接

                   private void closeConnection() {

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             ServerConnection sc = (ServerConnection)selectNode.getUserObject();

                             //將ServerConnection的連接對象設為null

                             sc.setConnection(null);

                             //刪除所有的子節(jié)點

                             removeNodeChildren(selectNode);

                             //設置樹不選中

                             this.tree.setSelectionPath(null);

                   }

          以上代碼的removeNodeChildren方法,從樹中刪除一個節(jié)點的所有子節(jié)點,用戶選擇了某個節(jié)點再進行刪除節(jié)點后就會觸發(fā)closeConnection方法。

          5 數(shù)據(jù)庫管理

          數(shù)據(jù)庫管理功能不多,包括打開數(shù)據(jù)庫、關閉數(shù)據(jù)庫和刪除數(shù)據(jù)庫,這三個功能與連接管理中的功能類似,例如打開連連與打開數(shù)據(jù)庫,都需要創(chuàng)建子節(jié)點,但是每個數(shù)據(jù)庫的子節(jié)點都只有三個,分別是表節(jié)點、視圖節(jié)點和存儲過程節(jié)點,而連接則是根據(jù)數(shù)據(jù)庫來創(chuàng)建子節(jié)點的。在本章中,我們使用一個Database對象來代表一個數(shù)據(jù)庫節(jié)點,具體請看2.2中的Database類。Database對象與ServerConnection對象都是繼承于ConnectionNode的,因此都需要去實現(xiàn)connect方法,并都有一個屬性自己的Connection對象。實現(xiàn)打開數(shù)據(jù)庫或者關閉數(shù)據(jù)庫功能時,都與ServerConnection的實現(xiàn)類似。

          5.1 打開數(shù)據(jù)庫

          當數(shù)據(jù)庫節(jié)點被點擊后,就可以進行打開操作,在4.2中,當樹的某個節(jié)點被點擊后,就會調(diào)用MainFrame的viewTreeDatas方法,在4.2中,我們只判斷了用戶點擊服務器節(jié)點的情況,下面再幫該方法加入判斷數(shù)據(jù)庫節(jié)點的情況,當然,該方法還需要加入表節(jié)點、視圖節(jié)點和存儲過程節(jié)點的點擊判斷,判斷用戶點擊的是哪種類型節(jié)點,再進行處理。

          MainFrame的viewTreeDatas方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                             //判斷點擊節(jié)點的類型

                             if (selectNode.getUserObject() instanceof ServerConnection) {

                                      clickServerNode(selectNode);//服務器連接節(jié)點,在4.2中已經(jīng)實現(xiàn)

                             } else if (selectNode.getUserObject() instanceof Database) {

                                      clickDatabaseNode(selectNode);//數(shù)據(jù)庫連接節(jié)點

                             }

          以上的代碼判斷了用戶點擊節(jié)點的類型,以下是上面代碼中clickDatabaseNode的實現(xiàn),點擊數(shù)據(jù)庫節(jié)點后,需要進行數(shù)據(jù)庫連接,再為數(shù)據(jù)庫節(jié)點添加三個子節(jié)點(表、視圖和存儲過程)。

          MainFrame的clickDatabaseNode方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //創(chuàng)建數(shù)據(jù)庫節(jié)點子節(jié)點

                   private void buildDatabaseChild(Database database,

                                      DefaultMutableTreeNode databaseNode) {

                             //判斷如果已經(jīng)連接,則不創(chuàng)建節(jié)點

                             if (databaseNode.getChildCount() != 0) return;

                             //創(chuàng)建三個子節(jié)點(表、視圖、存儲過程)

                             DefaultMutableTreeNode tableNode = new DefaultMutableTreeNode(new TableNode(database));

                             DefaultMutableTreeNode viewNode = new DefaultMutableTreeNode(new ViewNode(database));

                             ProcedureNode pNode = new ProcedureNode(database);

                             DefaultMutableTreeNode procedureNode = new DefaultMutableTreeNode(pNode);

                             //插入樹中

                             this.treeModel.insertNodeInto(tableNode, databaseNode, databaseNode.getChildCount());

                             this.treeModel.insertNodeInto(viewNode, databaseNode, databaseNode.getChildCount());

                            this.treeModel.insertNodeInto(procedureNode, databaseNode, databaseNode.getChildCount());

                   }

                   //點擊數(shù)據(jù)庫節(jié)點

                   public void clickDatabaseNode(DefaultMutableTreeNode selectNode) {

                             //獲取點擊樹節(jié)點的對象

                             Database database = (Database)selectNode.getUserObject();

                             validateConnect(selectNode, database);

                             //創(chuàng)建節(jié)點

                             buildDatabaseChild(database, selectNode);

                   }

          點擊數(shù)據(jù)庫節(jié)點與點擊連接節(jié)點的實現(xiàn)類似,都是先進行驗證連接,驗證都是調(diào)用ConnectionNode的connect方法進行,而這個方法都由ServerConnection和Database分別進行實現(xiàn)。驗證了連接后,再進行創(chuàng)建節(jié)點,數(shù)據(jù)庫節(jié)點的子節(jié)點只有三個:表、視圖和存儲過程,以上代碼的黑體部分創(chuàng)建這三個子節(jié)點。以下是Database對父類的connect方法的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

                   //創(chuàng)建本類的連接對象

                   public Connection connect() {

                             //如果已經(jīng)連接, 則返回

                             if (super.connection != null) return super.connection;

                             //創(chuàng)建數(shù)據(jù)庫連接

                             super.connection = this.serverConnection.createConnection(this.databaseName);

                             return super.connection;

                   }

          我們在2.2中創(chuàng)建Database對象時,為該對象指定了一個構造器,構造Database對象必須要一個ServerConnection對象,表明一個Database所屬的服務器連接,因為我們可以直接使用ServerConnection的createConnection方法去創(chuàng)建Connection連接,createConnection方法在4.2中已經(jīng)實現(xiàn)。

          5.2 新建數(shù)據(jù)庫

          新建一個數(shù)據(jù)庫,使用JDBC執(zhí)行CREATE DATABASE即可實現(xiàn),當用戶選擇了一個連接節(jié)點的時候,就可以選擇彈出的右鍵菜單來創(chuàng)建數(shù)據(jù)庫,如圖11所示,接下來顯示數(shù)據(jù)庫創(chuàng)建界面,該界面只有一個JTextField,給用戶去輸入數(shù)據(jù)庫名稱,在本章對應的是DatabaseFrame類。為Database對象新建一個create方法,該方法用于創(chuàng)建數(shù)據(jù)庫。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

                   //創(chuàng)建數(shù)據(jù)庫

                   public void create() {

                             Statement stmt = this.serverConnection.getStatement();

                             stmt.execute("create database " + this.databaseName);

                   }

          為DatabaseFrame的確定按鈕加入監(jiān)聽器并調(diào)用Database的create方法即可,需要注意的是,顯示DatabaseFrame的時候,需要將當前選擇的連接節(jié)點對象(ServerConnection)也傳遞到DatabaseFrame中。創(chuàng)建了數(shù)據(jù)庫后,以Database對象來創(chuàng)建一個樹節(jié)點,添加到相應的連接節(jié)點下。

          5.3 刪除數(shù)據(jù)庫

          刪除數(shù)據(jù)庫,只需要使用JDBC執(zhí)行DROP DATABASE語句即可實現(xiàn),以下是刪除數(shù)據(jù)庫的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

                   //刪除一個數(shù)據(jù)庫

                   public void remove() {

                             Statement stmt = this.serverConnection.getStatement();

                             stmt.execute("drop database " + this.databaseName);

                   }

          刪除數(shù)據(jù)庫后,還需要將該節(jié)點從樹上刪除。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //刪除一個數(shù)據(jù)庫

                   private void removeDatabase() {

                             //得到選擇中的節(jié)點

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             Database db = (Database)selectNode.getUserObject();

                             db.remove();

                             this.treeModel.removeNodeFromParent(selectNode);

                   }

          5.4 關閉數(shù)據(jù)庫

          與4.5中關閉連接一樣,都是將本類中的connection屬性設置為null,再將子節(jié)點全部刪除。以下是關閉數(shù)據(jù)庫的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //關閉數(shù)據(jù)庫連接

                   private void closeDatabase() {

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             Database db = (Database)selectNode.getUserObject();

                             db.setConnection(null);

                             //刪除所有的子節(jié)點

                             removeNodeChildren(selectNode);

                             //設置樹不選中

                             this.tree.setSelectionPath(null);

                   }

          以上代碼的黑體部分已經(jīng)在4.5中實現(xiàn)。到此,數(shù)據(jù)庫的相關管理功能已經(jīng)實現(xiàn),數(shù)據(jù)庫的功能相對比較簡單,只需要使用CREATE DATABASE和DROP DATABASE即可實現(xiàn),如果需要修改數(shù)據(jù)庫,可以使用ALTER DATABASE實現(xiàn)。

          6 視圖管理

          視圖管理主要包括讀取視圖、新建視圖、修改視圖和查詢視圖。當用戶選擇了某個數(shù)據(jù)庫,并點工具欄的視圖菜單或者點擊視圖節(jié)點,就可以查詢?nèi)康囊晥D,再選擇某個具體的視圖,點擊鼠相當規(guī)模右鍵,就彈出相關的右鍵菜單,具體的菜單在2.10中已經(jīng)提供(圖14)。

          6.1 讀取視圖列表

          用戶選擇了某個數(shù)據(jù)庫節(jié)點后,就可以打開這個數(shù)據(jù)庫的連接,再點擊這個數(shù)據(jù)庫節(jié)點下面的視圖節(jié)點,就可以查詢這個數(shù)據(jù)庫中所有的視圖。在4.2中,當點擊了樹中的某個節(jié)點時,我們就會執(zhí)行一個viewTreeDatas方法,該方法在5.1中也實現(xiàn)了數(shù)據(jù)庫節(jié)點的點擊,現(xiàn)在再為該方法加入點擊視圖節(jié)點的實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //點擊樹節(jié)點的操作

                   public void viewTreeDatas() {

                             //獲得選中的節(jié)點

                             DefaultMutableTreeNode selectNode = getSelectNode();

                             if (selectNode == null) return;

                             //清空列表數(shù)據(jù)

                             this.dataList.setListData(this.emptyData);

                             //判斷點擊節(jié)點的類型

                             if (selectNode.getUserObject() instanceof ServerConnection) {

                                      clickServerNode(selectNode);//服務器連接節(jié)點, 在4.2中實現(xiàn)

                             } else if (selectNode.getUserObject() instanceof Database) {

                                      clickDatabaseNode(selectNode);//數(shù)據(jù)庫連接節(jié)點, 在5.1中實現(xiàn)

                             } else if (selectNode.getUserObject() instanceof ViewNode) {

                                      Database db = getDatabase(selectNode);

                                      clickViewNode(db);//視圖節(jié)點

                             }

                   }

          在本章中,數(shù)據(jù)列表由一個JList實現(xiàn),由于點擊了視圖節(jié)點會在JList中顯示視圖數(shù)據(jù),因此我們需要將JList中原有的數(shù)據(jù)清空(以上代碼的黑體部分)。當用戶選擇的是一個視圖節(jié)點,那么就會執(zhí)行clickViewNode方法(該方法在下面實現(xiàn)),該方法主要去讀取數(shù)據(jù)庫中的所有視圖,再將數(shù)據(jù)放入JList中,我們將讀取數(shù)據(jù)庫視圖的方法寫在Database中。要查詢所有的視圖,我們需要到MySQL內(nèi)置的數(shù)據(jù)庫information_schema中的VIEWS表查詢,該表保存了MySQL中所有的視圖,因此查詢的時候,我們需要加入數(shù)據(jù)庫名稱作為查詢條件。

          Database中查詢視圖代碼。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

                   //返回數(shù)據(jù)為中所有的視圖

                   private ResultSet getViewsResultSet() throws Exception {

                             Statement stmt = getStatement();

                             //到information_schema數(shù)據(jù)庫中的VIEWS表查詢

                             String sql = "SELECT * FROM information_schema.VIEWS sc WHERE " +

                                               "sc.TABLE_SCHEMA='" + this.databaseName + "'";

                             ResultSet rs = stmt.executeQuery(sql);

                             return rs;

                   }

          以上代碼執(zhí)行一句查詢的SQL并返回ResultSet對象,但是這樣并不滿足要求,我們需要將ResultSet對象轉換成界面顯示的數(shù)據(jù)格式。在列表中,我們使用一個ViewData代表一個視圖,ViewData對象在2.2中已經(jīng)創(chuàng)建,該對象包含一個database和一個content(String類型)屬性,database屬性代表這個ViewData對象所屬的數(shù)據(jù)庫,content屬性表示這個視圖的內(nèi)容,以下代碼將ResultSet對象封裝成ViewData集合。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobject reeDatabase.java

                   //返回這個數(shù)據(jù)庫里的所有視圖

                   public List<ViewData> getViews() {

                             List<ViewData> result = new ArrayList<ViewData>();

                             ResultSet rs = getViewsResultSet();

                             while (rs.next()) {

                                      //得到視圖的定義內(nèi)容

                                      String content = rs.getString("VIEW_DEFINITION");

                                      ViewData td = new ViewData(this, content);

                                      //得到視圖名稱

                                      td.setName(rs.getString(TABLE_NAME));

                                      result.add(td);

                             }

                             rs.close();

                             return result;

                   }

          得到了視圖對象的集合后,我們就可以實現(xiàn)點擊視圖節(jié)點的方法(本小節(jié)前面的clickViewNode方法),將得到的視圖集合放入JList中,并創(chuàng)建相應的右鍵菜單(圖14)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiMainFrame.java

                   //點擊視圖節(jié)點,查找全部的視圖

                   private void clickViewNode(Database db) {

                             List<ViewData> datas = db.getViews();

                             this.dataList.setListData(datas.toArray());

                             //顯示視圖后,創(chuàng)建右鍵菜單

                             createViewMenu();

                             //設置當前顯示的數(shù)據(jù)類型為視圖

                             this.currentView = new ViewData(db, null);

                   }

          注意最后還需要將當前的ViewObject設置為視圖對象,用于標識當前所瀏覽的數(shù)據(jù)類型。實現(xiàn)效果如圖16所示。

          圖16 視圖列表

          6.2 新建視圖

          創(chuàng)建視圖使用JDBC執(zhí)行CREATE VIEW語句即可實現(xiàn),視圖界面只提供一個保存功能,由于我們創(chuàng)建視圖與修改視圖都是使用同一個界面,因此在執(zhí)行保存的時候,就需要判斷新增還是修改。當?shù)玫接脩粼谝晥D界面輸入的視圖定義后,就可以執(zhí)行CREATE VIEW語句進行創(chuàng)建視圖。為ViewData對象加入一個創(chuàng)建視圖的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistViewData.java

                   //創(chuàng)建視圖

                   public void createView() {

                             //拼裝CREATE VIEW語句

                             String sql = MySQLUtil.CREATE_VIEW + name + " " +

                             MySQLUtil.AS + " " + content;

                             database.getStatement().execute(sql);

                   }

          用戶點擊保存,彈出另外一個窗口讓用戶輸入視圖名稱,最后調(diào)用上面的createView方法,即可以創(chuàng)建視圖,

          6.3 修改視圖與刪除視圖

          與創(chuàng)建視圖一樣,使用同樣的界面,只是執(zhí)行不同的SQL語句,修改視圖可以使用ALTER VIEW即可,為ViewData對象加入修改視圖的方法。當修改完視圖后,調(diào)用修改方法即可。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistViewData.java

                   //修改視圖

                   public void alterView() {

                             String sql = MySQLUtil.ALTER_VIEW + name + " " + MySQLUtil.AS

                                      + " " + content;

                             database.getStatement().execute(sql);

                   }

          同樣地,刪除視圖使用JDBC執(zhí)行DROP VIEW即可實現(xiàn)。

          ViewData:

                   //刪除視圖

                   public void dropView() {

                             String sql = MySQLUtil.DROP_VIEW + this.name;

                             database.getStatement().execute(sql);

                   }

          到此,查詢?nèi)恳晥D、創(chuàng)建視圖、修改視圖和刪除視圖功能已經(jīng)實現(xiàn),但是,還缺少一個最重要的功能,就是查看視圖。當我們選擇了某個視圖進行雙擊操作的時候,就需要瀏覽該視圖的數(shù)據(jù),這一個功能我們將在下面的章節(jié)中實現(xiàn)。

          7 存儲過程與函數(shù)管理

          存儲過程與函數(shù)管理使用相同的界面,因此我們可以一起實現(xiàn),它們的區(qū)別在于是否有返回值,通過一些界面判斷即可實現(xiàn)。與視圖管理一樣,都是有新增、修改和刪除功能。

          7.1 新增存儲過程和函數(shù)

          存儲過程和函數(shù)的界面已經(jīng)在2.7中創(chuàng)建,得到存儲過程或者函數(shù)的定義、參數(shù)、返回值(函數(shù))與名稱后,就可以使用命令去創(chuàng)建存儲過程或者函數(shù)。創(chuàng)建存儲過程使用CREATE PROCEDURE,創(chuàng)建函數(shù)使用CREATE FUNCTION。在本章中,視圖數(shù)據(jù)使用的是一個ViewData對象(6章節(jié)),在2.2中也創(chuàng)建了一個ProcedureData對象來表示一個存儲過程的數(shù)據(jù)對象,因此將創(chuàng)建存儲過程或者函數(shù)加入到該類中即可。

          ProcedureData創(chuàng)建存儲過程方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistProcedureData.java

                   //創(chuàng)建存儲過程

                   public void createProcedure() {

                             String sql = MySQLUtil.CREATE_PROCEDURE + this.name +

                             " (" + this.arg + ") " + this.content;

                             this.database.getStatement().execute(sql);

                   }

          ProcedureData中創(chuàng)建函數(shù)方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistProcedureData.java

                   //創(chuàng)建函數(shù)

                   public void createFunction() {

                             String sql = MySQLUtil.CREATE_FUNCTION + this.name +

                             " (" + this.arg + ") returns " + this.returnString + " " + this.content;

                             this.database.getStatement().execute(sql);

                   }

          存儲過程對象(ProcedureData)與視圖對象(ViewData)一樣,都是屬于某個數(shù)據(jù)庫的,因此這兩個對象都會保存一個數(shù)據(jù)庫的屬性,直接就可以通過數(shù)據(jù)庫對象(Database)的getStatement方法得到Statement對象,再執(zhí)行SQL語句。

          7.2修改存儲過程與函數(shù)

          修改存儲過程(函數(shù))與新增存儲過程(函數(shù))使用的是相同的界面,因此在保存的時候需要作出判斷。與修改視圖不同的是,修改存儲過程或者函數(shù),不使用ALTER PROCEDURE(ALTER FUNCTION)來實現(xiàn),這是由于MySQL中的ALTER PROCEDURE和ALTER FUNCTION并不能修改存儲過程或者函數(shù)的方法體與參數(shù),因此,實現(xiàn)時需要將原來的存儲過程或者函數(shù)先刪除,再重新創(chuàng)建。

          ProcedureData中修改存儲過程。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistProcedureData.java

                   //修改存儲過程

                   public void updateProcedure() {

                             //修改存儲過程需要先把原來的先刪除

                             //刪除語句

                             String dropSQL = MySQLUtil.DROP_PROCEDURE + this.name;

                             this.database.getStatement().execute(dropSQL);

                             //創(chuàng)建語句

                             String createSQL = MySQLUtil.CREATE_PROCEDURE + this.name +

                             " (" + this.arg + ") " + this.content;

                             this.database.getStatement().execute(createSQL);

                   }

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistProcedureData.java

                   //修改函數(shù)

                   public void updateFunction() {

                             //修改需要先把原來的先刪除

                            String dropSQL = MySQLUtil.DROP_FUNCTION + this.name;

                             this.database.getStatement().execute(dropSQL);

                             String createSQL = MySQLUtil.CREATE_FUNCTION + this.name +

                             " (" + this.arg + ") returns " + this.returnString + " " + this.content;

                             this.database.getStatement().execute(createSQL);

                   }

          以上兩個方法就可以修改存儲過程和函數(shù),但是如果一旦存儲過程或者函數(shù)編寫有誤,那么就會將原來的存儲過程或者函數(shù)刪除,為了解決這個問題,可以將原來的存儲過程改名,再創(chuàng)建一個修改后的存儲過程,如果創(chuàng)建失敗,就將改名后的舊的存儲過程改回來,這樣就可以確保錯誤發(fā)生后無法恢復原來的存儲過程。修改存儲過程或者函數(shù)的名稱使用ALTER PROCEDURE或者ALTER FUNCTION即可實現(xiàn)。

          在修改存儲過程與函數(shù)的時候,我們就使用了DROP PROCEDURE和DROP FUNCTION來刪除一個存儲過程和函數(shù),刪除存儲過程和函數(shù)不再詳細描述。存儲過程或者函數(shù)的調(diào)用可以使用CALL來調(diào)用,在實現(xiàn)了SQL查詢功能后,就可以執(zhí)行一句CALL的SQL來調(diào)用查看效果。

          8 表管理

          表管理在本章中相對較難,我們需要從界面中得到創(chuàng)建表的信息,例如字段信息、外鍵字段信息等。在修改表的時候,用戶在界面的表格中會進行各種操作,操作完后進行保存,就需要收集這些被操作過的數(shù)據(jù),再進表的修改。

          在2.5中,我們已經(jīng)創(chuàng)建的表管理界面(如2.5中的圖7所示),對應的是TableFrame類,界面中存的有兩個列表對象,分別是字段列表和外鍵字段列表,對應的類是FieldTable與ForeignTable,它們都繼承于JTable。字段列表有以下操作:

                新字段:向字段列表的尾部追加一個新的字段行。

                插入字段:在所選擇的行前面插入一個字段行。

                刪除字段:刪除所選的行。

                設置默認值:在文本框中輸入該字段的默認值。

                設置自動增長:設置字段是否可以自動增長。

          外鍵字段有以下操作:

                新外鍵:向表外尾部追加一個新的外鍵。

                刪除外鍵:刪除一個選擇的外鍵。

          8.1 新字段

          為了能體現(xiàn)一個字段,我們新建一個字段對象Field,該對象保存一個字段的所有信息,包括名稱,長度等一系列的字段信息。

          代碼清單:codemysql-managersrcorgcrazyitmysql ableobjectField.java

                   private String fieldName; //字段名

                   private String type; //字段類型

                   private boolean allowNull = true; //允許空,默認允許為空

                   private boolean isPrimaryKey = false; //是否主鍵,是主鍵為true,否則為false

                   private String defaultValue; //默認值

                   private boolean autoIncrement = false; //是否自動增長

                   private TableData table; //該字段所屬的表         

                   private String uuid; //標識這個字段的uuid

                   //省略setter和getter方法

          接下來,在TableFrame(表管理界面)中,創(chuàng)建一個字段集合用來保存當前界面所顯示的字段,那么如果進行新建字段操作,就可以對該集合進行操作了。下面實現(xiàn)刷新字段列表的方法,由于在加入新字段、修改字段或者刪除字段后,都需要將列表進行一次刷新。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //刷新字段列表

                   public void refreshFieldTable() {

                             DefaultTableModel tableModel = (DefaultTableModel)this.fieldTable.getModel();

                             //設置數(shù)據(jù)

                             tableModel.setDataVector(getFieldDatas(), this.fieldTable.getFieldTableColumn());

                             //設置列表樣式,包括行高、列寬等

                             this.fieldTable.setTableFace();

                   }

                   //得到字段列表數(shù)據(jù)

                   public Vector getFieldDatas() {

                             Vector datas = new Vector();

                             for (int i = 0; i < this.fields.size(); i++) {

                                      Field field = this.fields.get(i);

                                      Vector data = new Vector();

                                      data.add(field.getFieldName());//字段名稱

                                      data.add(field.getType());//字段類型

                                      data.add(getNullIcon(field));//獲得是否允許空的圖片

                                      data.add(getPrimaryKeyIcon(field));//獲得主鍵圖片

                                      datas.add(data);

                             }

                             return datas;

                   }

          以上代碼中的黑體部分,就是當前界面中的字段集合。在新建一個字段后,就可以使用refreshFieldTable方法對字段列表進行刷新。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //加入新字段

                   private void newField() {

                             Field field = new Field();

                             this.fields.add(field);

                             //刷新字段列表

                             refreshFieldTable();

                             //如果是修改狀態(tài),則添加addFields集合中

                             if (this.table.getName() != null) this.addFields.add(field);

                   }

          以上的黑體代碼,是在用戶是行修改表的時候才需要,我們創(chuàng)建一個addFields集合,用來保存用戶添加過的字段(修改的時候)。該集合的作用,我們將修改表的時候詳細描述。

          8.2 插入字段與刪除字段

          插入新字段,只需要得到用戶當前所選擇的行,并在該行的前面加入一個數(shù)據(jù)行即可,需要注意的是,當用戶沒有選擇任意一行的時候,就可以調(diào)用新字段的方法,即8.1中創(chuàng)建新字段的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //插入新字段

                   private void insertField() {

                             //得到選擇中的行索引

                             int selectRow = this.fieldTable.getSelectedRow();

                             if (selectRow == -1) {

                                      //沒有選中,調(diào)用加新字段方法,加入新字段

                                      newField();

                                      return;

                             }

                             Field field = new Field();

                             this.fields.add(selectRow, field);

                             //刷新字段列表

                             refreshFieldTable();

                             //如果是修改狀態(tài),則添加addFields集合中

                             if (this.table.getName() != null) this.addFields.add(field);

                   }

          刪除字段實現(xiàn)與插入字段一樣,只需要將字段從集合中刪除并刷新列表即可,另外,如果是修改表的話,就需要加入另外的操作,在下面修改表的章節(jié)中描述。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //刪除字段

                   private void deleteField() {

                             //得到選中的行

                             int selectRow = this.fieldTable.getSelectedRow();

                            if (selectRow == -1) return;

                             //得到用戶所選擇的Field對象

                             Field field = this.fields.get(selectRow);

                             if (field == null) return;

                             //從字段集合中刪除

                             this.fields.remove(field);

                             //刷新列表

                             refreshFieldTable();

                   }

          8.3 編輯字段

          當用戶在字段列表中對字段的信息進行修改時,就需要得到相應的Field對象,并設置新的信息。當用戶對列表停止編輯的時候,就可以觸發(fā)相應的方法。這里需要注意的是,當停止編輯的時候,需要修改對應的Field對象,只需要修改該對象的字段名稱與字段類型,因為這兩個屬性才可以輸入,其他兩個屬性(是否允許空和主鍵)進行選擇才會發(fā)生值的改變。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTable.java

                   //重寫JTable的方法, 列表停止編輯的時候觸發(fā)該方法

                   public void editingStopped(ChangeEvent e) {

                             int column = this.getEditingColumn();

                             int row = this.getEditingRow();

                             super.editingStopped(e);

                             //獲得當前編輯的列名

                             DefaultTableModel model = (DefaultTableModel)this.getModel();

                             String columnName = model.getColumnName(column);

                             //得到編輯后的單元格的值

                            String value = (String)this.getValueAt(row, column);

                             if (columnName.equals(FIELD_NAME)) {

                                      //更改字段名稱

                                      this.tableFrame.changeFieldName(row, value);

                             } else if (columnName.equals(FIELD_TYPE)) {

                                      //更改字段類型

                                      this.tableFrame.changeFieldType(row, value);

                             }

                   }

          在列表停止編輯的時候,得到用戶所編輯的單元格的行索引和編輯后的值,再調(diào)用TableFrame的方法進行修改字段名和字段類型。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //字段列表的字段名稱,同步去修改字段集合中的字段名稱值

                   public void changeFieldName(int row, String value) {

                             //得到相應的Field對象

                             Field field = this.fields.get(row);

                             if (field == null) return;

                             field.setFieldName(value);

                   }

                   //字段列表的字段類型,同步去修改字段集合中的字段類型值

                   public void changeFieldType(int row, String value) {

                             //得到相應的Field對象

                             Field field = this.fields.get(row);

                             if (field == null) return;

                             field.setType(value);

                   }

          那么用戶對列表進行修改后,就可以同步的去修改TableFrame中的相應對象的字段名和字段類型。另外,如果用戶點擊了“允許空”和“主鍵”列,就需要同步去修改集合中的Field對象。修改FieldTable的selectCell方法,該方法在2.5中已經(jīng)實現(xiàn)了部分,當用戶選擇了列表的時候,就會觸發(fā)該方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTable.java

                   //鼠標在JTable中點擊的時候觸發(fā)該方法

                   private void selectCell() {

                             int column = this.getSelectedColumn();

                             int row = this.getSelectedRow();

                             if (column == -1 || row == -1) return;

                             //修改圖片列

                             selectAllowNullColumn(row, column);

                             selectPrimaryKeyColumn(row, column);

                             //設置點擊后會改變的值,注意是點擊,并不是輸入,因此只會更改允許空和主鍵

                             changeClickValue(row, column);

                   }

          以上的黑體代碼為新加的代碼。其中的changeClickValue為改變“允許空”與“主鍵”這兩列的值,當用戶點擊了這兩列的時候,就需要同步修改TableFrame的fields集合。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTable.java

                   //當發(fā)生鼠標點擊單元格事件的時候,改變值,一般只改變允許空和主鍵列

                   private void changeClickValue(int row, int column) {

                             //得到主鍵列

                             TableColumn primaryColumn = this.getColumn(PRIMARY_KEY);

                             if (primaryColumn.getModelIndex() == column) {

                                      this.tableFrame.changePrimaryKeyValue(row);

                             }

                             //得到允許空列

                             TableColumn allowNullColumn = this.getColumn(ALLOW_NULL);

                             if (allowNullColumn.getModelIndex() == column) {

                                      this.tableFrame.changeAllowNullValue(row);

                             }

                   }

          判斷用戶所點擊的單元格所屬的列,如果是“允許空”或者“主鍵”列,就可以調(diào)用TableFrame中的方法去修對應Field對象的屬性值。

          8.4 設置默認值與自動增長

          界面中提供了一個文本框,可以設置某個字段的默認值,提供了一個多選框,可以設置字段是否可以自動增長。我們可以為文本框加入按鍵事件,當用戶在文本框中進行輸入時,就可以改變該字段的默認值。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //改變字段的默認值

                   public void changeDefaultValue() {

                             //得到選中的行

                             int selectRow = this.fieldTable.getSelectedRow();

                             if (selectRow == -1) return;

                             //取得默認值

                             String defaultValue = this.defaultField.getText();

                             //取得當前編輯的Field對象

                             Field field = this.fields.get(selectRow);

                             //設置字段默認值

                             field.setDefaultValue(defaultValue);

                   }

          同樣地,與設置默認值一樣,也可以為多選框加入監(jiān)聽器,如果發(fā)生點擊事件時,就執(zhí)行某個方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //點擊自動增長checkBox的方法

                   private void clickIsAutoIncrementBox() {

                             //得到字段列表中所選中的行索引

                             int row = this.fieldTable.getSelectedRow();

                             if (row == -1) return;

                             //得到當前所選擇了Field對象

                             Field field = this.fields.get(row);

                             //設置Field對象中的自動增長屬性

                             if (this.isAutoIncrementBox.isSelected()) field.setAutoIncrement(true);

                             else field.setAutoIncrement(false);

                   }

          那么用戶在設置默認值或者自動增長的時候,就可以同步將默認值與自動增長的標識保存到TableFrame的fields集合中。但是,還需要去修改點擊字段列表的方法,將默認值與自動增長的值展現(xiàn)到界面的元素中。修改FieldTable的selectCell方法,列表被點擊時,會觸發(fā)該方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableFieldTable.java

                   //鼠標在JTable中點擊的時候觸發(fā)該方法

                   private void selectCell() {

                             //省略其他代碼

                             //修改默認值

                             this.tableFrame.setDefaultValue(row);

                             //修改是否自動增長的checkbox

                             this.tableFrame.setIsAutoIncrement(row);

                   }

          點擊了列表某行的時候,就可以得到相應的Field對象,再設置界面的文本框和多選框即可。

          8.5 新外鍵

          與實現(xiàn)新字段一樣,新建一個FoeignField對象來代表一個外鍵,并在TableFrame中創(chuàng)建一個集合來保存當前界面中的外鍵。

          代碼清單:codemysql-managersrcorgcrazyitmysql ableobjectForeignField.java

                   private String constraintName; //約束的名稱

                   private Field field; //被約束的字段,根據(jù)該字段可以找出該外鍵對象所屬于的表

                   private Field referenceField; //外鍵的字段,可以根據(jù)此屬性找出該關系中的外鍵表

                   private String onDelete; //級聯(lián)刪除策略

                   private String onUpdate; //級聯(lián)更新策略

                   private String referenceTableName; //約束表的名稱

                   private String referenceFieldName; //約束字段的名稱

                   private String uuid; //字段的uuid

                   //省略setter和getter方法

          在2.5中已經(jīng)創(chuàng)建了外鍵列表對象(ForeignTable),新建刷新外鍵列表的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //刷新外鍵字段列表

                   public void refreshForeignFieldTable() {

                             //設置外鍵數(shù)據(jù)

                             DefaultTableModel tableModel = (DefaultTableModel)this.foreignTable.getModel();

                             tableModel.setDataVector(getForeignDatas(), this.foreignTable.getForeignColumns());

                             //設置外鍵列表的樣式

                             this.foreignTable.setTableFace();

                   }

          加入一個外鍵的時候,就可以調(diào)用refreshForeignFieldTable方法刷新外鍵列表。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //新增一個外鍵字段

                   private void newForeignField() {

                             ForeignField foreignField = new ForeignField();

                             this.foreignFields.add(foreignField);

                             //設置該外鍵的constraintName,用UUID設置

                             foreignField.setConstraintName(UUID.randomUUID().toString());

                             //刷新外鍵列表

                             refreshForeignFieldTable();

                             //如果是修改狀態(tài),加到添加的外鍵集合中

                             if (this.table.getName() != null) this.addForeignFields.add(foreignField);

                   }

          以上的黑體代碼,與8.1中新字段一樣,都是在修改表的時候需要使用到的代碼,將在下面修改表的章節(jié)中加以描述。這里需要注意的是,為了標識新加的“外鍵”在數(shù)據(jù)庫中的唯一性,因為需要將ForeignField的constraintName屬性設置為唯一的。

          8.6 刪除一個外鍵

          刪除外鍵實現(xiàn)比較簡單,只需要從TableFrame的“外鍵”集合中刪除即可。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiTableFrame.java

                   //刪除一個字段

                   private void deleteForeignField() {

                             //得到選中的行

                             int selectRow = this.foreignTable.getSelectedRow();

                             if (selectRow == -1) return;

                             //得到選中的外鍵對象

                             ForeignField field = this.foreignFields.get(selectRow);

                             if (field == null) return;

                             //從字段集合中刪除

                             this.foreignFields.remove(field);

                   }

          前面幾個小節(jié)中,我都講解了如何實現(xiàn)表管理中的一些界面操作,接下來實現(xiàn)具體的表管理,包括查詢表信息、保存表和修改表。

          8.7 查詢字段信息

          查詢一個表的信息需要到MySQL的系統(tǒng)表中查詢,這些信息包括字段信息與外鍵信息等。由于我們在TableFrame中建立了一個字段集合來保存當前界面中的字段信息,因此,只需要從數(shù)據(jù)庫中查詢所有的表字段并封裝成一個Field集合即可。在本章中,一個表由一個TableData對象來代表,TableData中包含了一個Database對象,Database對象可以取到數(shù)據(jù)庫的連接信息,可以將查詢字段的方法寫到TableData中。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //獲得查詢字段的SQL

                   private String getFieldSQL() {

                             StringBuffer sql = new StringBuffer();

                             sql.append("SELECT * FROM information_schema.COLUMNS sc")

                             .append(" WHERE sc.TABLE_SCHEMA='")

                             .append(this.database.getDatabaseName() + "' ")

                             .append(" AND sc.TABLE_NAME='")

                             .append(this.name + "' ")

                             .append("ORDER BY sc.ORDINAL_POSITION");

                             return sql.toString();

                   }

          getFieldSQL方法是返回一句查詢的SQL,到系統(tǒng)表中查詢一個表的所有字段,使用JDBC執(zhí)行getFieldSQL方法返回的SQL,就可以得到ResultSet對象,再得到各個列的值。

          我們需要從查詢到的ResultSet中得到以下值:

                COLUMN_NAME:字段名。

                COLUMN_TYPE:字段類型。

                IS_NULLABLE:是否允許空。

                COLUMN_KEY:如果是主鍵,那么該值為“PRI”。

                COLUMN_DEFAULT:該字段的默認值。

                EXTRA:如果該值為auto_increment,則表示是自動增長。

          得到以上的值,就可以封裝成一個字段對象,并加到結果集合中,那么TableFrame就可以根據(jù)這個集合來顯示字段數(shù)據(jù)。

          8.8 查詢外鍵信息

          查詢外鍵信息與查詢字段信息一樣,都是到MySQL的系統(tǒng)表(KEY_COLUMN_USAGE)中進行查詢,但是,如果使用的MySQL5.0,則不能到系統(tǒng)表中查詢到一個外鍵的ON DELETE和ON UPDATE的值。如果使用的是MySQL5.1,就可以到系統(tǒng)表中查詢到一個字段的這兩個值。本章使用的MySQL版本是5.0,因此要得到ON DELETE和ON UPDATE的值,就需要得到建表時的SQL,并對該句SQL進行分析,得到外鍵的ON DELETE和ON UPDATE,這就是本章開頭所講的MySQL5.0與MySQL5.1的差別對我們開發(fā)這個管理器所產(chǎn)生的影響。

          執(zhí)行下面的SQL就可以返回外鍵字段信息的ResultSet:

          SELECT * FROM information_schema.KEY_COLUMN_USAGE sc WHERE sc.TABLE_SCHEMA='數(shù)據(jù)庫名' AND sc.TABLE_NAME='表名' AND sc.REFERENCED_COLUMN_NAME <> '' ORDER BY sc.COLUMN_NAME

          得到的ResultSet里面包含有如下字段:

                COLUMN_NAME:外鍵列名。

                CONSTRAINT_NAME:約束的名稱。

                REFERENCED_TABLE_NAME:約束的表名。

                REFERENCED_COLUMN_NAME:約束的字段名。

          得到約束的字段名后,就可以再次到系統(tǒng)表(COLUMNS)中查詢約束的字段。得到這些信息后,就可以創(chuàng)建一個ForeignField對象,但是,F(xiàn)oreignField中還包含有onDelete和onUpdate兩個屬性,為了得到這個屬性,我們需要得到創(chuàng)建表的SQL,并對該句SQL進行分析。如果你使用的是MySQL5.1,就可以直接到系統(tǒng)表中查詢。得到創(chuàng)建表的SQL,可以使用JDBC執(zhí)行SHOW CREATE TABLE來實現(xiàn)。

          以下方法分析創(chuàng)建表的SQL,并得到ON DELETE和ON UPDATE信息。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //返回ON DELETE或者ON UPDATE的值

                   private String getOnValue(String createSQL, ForeignField foreignField,

                                      String on) {

                             String constraintName = foreignField.getConstraintName();

                             //以逗號將其分隔

                             String[] temp = createSQL.split(",");

                             for (int i = 0; i < temp.length; i++) {

                                      String tempString = temp[i];

                                      //如果遇到外鍵的字符串,則進行處理

                                      if (tempString.indexOf("CONSTRAINT `" + constraintName + "`") != -1) {

                                               //如果遇到ON DELETE或者ON UPDATE,則進行處理,返回ON DELETE或者ON UPDATE的值

                                               if (tempString.indexOf(on) != -1) {

                                                         //得到ON DELETE或者ON UPDAT的位置

                                                         int onIndex = tempString.indexOf(on) + on.length() + 1;

                                                         String value = tempString.substring(onIndex, onIndex + 7);

                                                         if (value.indexOf("NO ACTI") != -1) return "NO ACTION";

                                                         else if (value.indexOf("RESTRIC") != -1)return "RESTRICT";

                                                         else if (value.indexOf("CASCADE") != -1) return "CASCADE";

                                                         else if (value.indexOf("SET NUL") != -1)return "SET NULL";

                                               }

                                      }

                             }

                             return null;

                   }

          得到各個信息并封裝成ForeignField的集合后,就可以將這個集合放到TableFrame中。TableFrame中保存了一個外鍵字段的集合,在8.5新外鍵中創(chuàng)建該集合。得到字段與外鍵字段的集合后,就可以在TableFrame中初始化列表,使用DefaultTableModel的setDataVector方法即可初始列表數(shù)據(jù)。

          8.9 新建表

          我們可以TableFrame(表管理界面)中得到字段的集合,集合中保存了一系列的Field對象,那么我們在創(chuàng)建表的時候,就可以根據(jù)這些Field對象的屬性來拼裝CREATE TABLE的SQL語句,然后使用JDBC執(zhí)行即可。

          編寫拼裝SQL的程序,最終得出的SQL語句如下:

          CREATE TABLE IF NOT EXISTS `table3` (`field1` int(10)AUTO_INCREMENT ,`field2` varchar(10)DEFAULT 'test' ,`field3` int(10),FOREIGN KEY (`field3`) REFERENCES `table_2` (`field_3`) ON DELETE CASCADE ON UPDATE CASCADE ,PRIMARY KEY(`field1`))

          具體實現(xiàn)在TableData中的addTable方法,拼裝SQL語句的方法是TableData中的getTableSQL方法,getTableSQL方法先拼裝CREATE TABLE命令,再依次拼裝字段的SQL、外鍵字段的SQL和創(chuàng)建主鍵的SQL。

          拼裝字段SQL的方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //根據(jù)字段創(chuàng)建SQL

                   private void createField(StringBuffer sql, List<Field> fields) {

                             for (Field f : fields) {

                                      sql.append("`" + f.getFieldName() + "` ")

                                      .append(f.getType());

                                      //自增長加入AUTO_INCREMENT

                                      if (f.isAutoIncrement()) sql.append(AUTO_INCREMENT + " ");

                                      //該字段不允許為空, 加入NOT NULL

                                      if (!f.isAllowNull()) sql.append(NOT_NULL + " ");

                                      //該字段有默認值,并且不是自動增長

                                      if (!f.isAutoIncrement()) {

                                               if (f.getDefaultValue() != null) sql.append(DEFAULT + " '" + f.getDefaultValue() + "' ");

                                      }

                                      sql.append(",");

                             }

                   }

          拼裝外鍵字段的SQL。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //創(chuàng)建外鍵SQL

                   private void createForeignFields(StringBuffer sql,

                                      List<ForeignField> foreignFields) {

                             for (ForeignField f : foreignFields) {

                                      sql.append(FOREIGN_KEY)

                                      .append(" (`" + f.getField().getFieldName() + "`) ")

                                      .append(REFERENCES)

                                      .append(" `" + f.getReferenceField().getTable().getName() + "` ")

                                      .append("(`" + f.getReferenceField().getFieldName() + "`) ");

                                      if (f.getOnDelete() != null) {

                                               sql.append(ON_DELETE + " " + f.getOnDelete() + " ");

                                      }

                                      if (f.getOnUpdate() != null) {

                                               sql.append(ON_UPDATE + " " + f.getOnUpdate() + " ");

                                      }

                                      sql.append(",");

                             }

                   }

          拼裝主鍵的SQL。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //創(chuàng)建主鍵SQL

                   private void createPrimary(StringBuffer sql, List<Field> fields) {

                             for (Field f : fields) {

                                      if (f.isPrimaryKey()) {

                                               sql.append(PRIMARY_KEY).append("(`" + f.getFieldName() + "`)").append(",");

                                      }

                             }

                   }

          得到創(chuàng)建表的SQL后,使用JDBC執(zhí)行即可。如果創(chuàng)建失敗,可以將異常信息顯示到界面中。

          8.10 修改表

          實現(xiàn)修改表的功能較為復雜,由于保存在界面中可能涉及的操作包括添加字段、修改字段、刪除字段、添加外鍵字段、修改外鍵字段和刪除外鍵字段,因此修改表的話,我們需要知道用戶添加、修改、刪除了哪些字段與外鍵字段。也就是說,用戶每次進行添加、修改、刪除字段與外鍵字段的時候,我們都需要對用戶所操作的數(shù)據(jù)進行保存,最后,得到這些數(shù)據(jù)后,就可以使用ALTER TALBE來進行表的修改。在8.1新字段中,創(chuàng)建一個字段的時候,另外建立了一個集合來保存用戶所添加的字段,因此還需要在修改和刪除操作的時候,保存用戶所操作的數(shù)據(jù)。

          得到用戶操作過的數(shù)據(jù)集合后,我們就可以為TableData加入一個修改表的方法,將這些集合里面的對象都轉換成SQL,然后使用JDBC執(zhí)行。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   /**

                    * 修改一個表

                    * @param addFields 需要添加的字段

                    * @param updateFields 修改的字段

                    * @param dropFields 刪除的字段

                    * @param addFF 添加的外鍵

                    * @param updateFF 修改的外鍵

                    * @param dropFF 刪除的外鍵

                    */

                   public void updateTable(List<Field> addFields, List<UpdateField> updateFields,

                                      List<Field> dropFields, List<ForeignField> addFF,

                                      List<UpdateForeignField> updateFF, List<ForeignField> dropFF) {

                             //得到添加字段的SQL

                             List<String> addFieldSQL = getAlterAddFieldSQL(addFields);

                             //得到修改字段的SQL

                             List<String> updateFieldSQL = getAlterUpdateFieldSQL(updateFields);

                             //得到刪除字段的SQL

                             List<String> dropFieldSQL = getAlterDropFieldSQL(dropFields);

                             //得到添加外鍵的SQL

                             List<String> addFFSQL = getAlterAddForeignFieldSQL(addFF);

                             //得到修改外鍵的SQL

                             List<String> updateFFSQL = getAlterUpdateForeignFieldSQL(updateFF);

                             //得到刪除外鍵的SQL

                             List<String> dropFFSQL = getAlterDropForeignFieldSQL(dropFF);

                             try {

                                      Statement stmt = database.getStatement();

                                      for (String s : addFieldSQL) stmt.addBatch(s);

                                      for (String s : updateFieldSQL) stmt.addBatch(s);

                                      for (String s : dropFieldSQL) stmt.addBatch(s);

                                      for (String s : addFFSQL) stmt.addBatch(s);

                                      for (String s : updateFFSQL) stmt.addBatch(s);

                                      for (String s : dropFFSQL) stmt.addBatch(s);

                                      stmt.executeBatch();

                             } catch (Exception e) {

                                      throw new QueryException("更改表錯誤:" + e.getMessage());

                             }

                   }

          以上的代碼將用戶操作過的對象轉換成SQL,例如,用戶為表添加了若干個字段,那么就需要這樣將Field對象轉換成修改的SQL。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //返回全部的ALTER TABLE ADD字段語句,參數(shù)為用戶添加的字段

                   private List<String> getAlterAddFieldSQL(List<Field> addFields) {

                             List<String> result = new ArrayList<String>();

                             for (Field f : addFields) {

                                      StringBuffer sql = new StringBuffer();

                                      sql.append("ALTER TABLE " + this.name).append(" ADD " + f.getFieldName())

                                      .append(" " + f.getType());

                                      if (!f.isAllowNull()) sql.append(" NOT NULL");

                                      if (f.getDefaultValue() != null) sql.append(" DEFAULT '" + f.getDefaultValue() + "'");

                                      if (f.isAutoIncrement()) sql.append(" AUTO_INCREMENT");

                                      if (f.isPrimaryKey()) sql.append(", ADD PRIMARY KEY (" + f.getFieldName() + ")");

                                      result.add(sql.toString());

                             }

                             return result;

                   }

          使用ALTER TABLE ADD來添加字段,那么如果修改了字段,就使用ALTER TABLE CHANGE,刪除字段使用ALTER TALBE DROP COLUMN。如果添加外鍵,需要先為約束字段加索引(ALTER TABLE ADD INDEX),再使用ALTER TABLE ADD FOREIGN KEY。如果是修改外鍵,就需要先將原來的外鍵刪除,再重新建一次外鍵,重新建外鍵的方法與添加外鍵的順序一致。刪除外鍵使用ALTER TABLE DROP FOREIGN KEY。

          修改表無論從界面操作到拼裝SQL都比較復雜,實現(xiàn)起來比較麻煩。在實現(xiàn)的過程中可以小步快跑,慢慢地一個一個來實現(xiàn)。

          8.11 刪除表

          刪除表實現(xiàn)十分簡單,只需要執(zhí)行DROP TABLE即可實現(xiàn),在TableData中加入一個dropTable方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistTableData.java

                   //刪除一個本類對應的表

                   public void dropTable() {

                             StringBuffer sql = new StringBuffer();

                             sql.append("DROP TABLE IF EXISTS " + this.name);

                             Statement stmt = database.getStatement();

                             stmt.execute(sql.toString());

                   }

          表管理的大部分功能都已經(jīng)實現(xiàn),以上實現(xiàn)的這些功能中,大部分實現(xiàn)原理都十分簡單,只是使用JDBC來執(zhí)行一些SQL語句即可,但是界面的交互比較復雜,在實現(xiàn)的時候需要特別注意。本章中還有打開表、導出表數(shù)據(jù)的功能,將在下面的章節(jié)中描述。

          9 數(shù)據(jù)瀏覽

          在6中,我們實現(xiàn)了視圖管理功能,在8中實現(xiàn)了表管理功能,在用戶的實際操作中,當雙擊一個視圖或者一個表的時候,我們需要將視圖或者表對應的數(shù)據(jù)展現(xiàn)給用戶,并在界面中顯示,數(shù)據(jù)顯示數(shù)據(jù)的界面已經(jīng)在2.3中創(chuàng)建(DataFrame類),如2.3中的圖4所示。該界面包括刷新、降序和升序功能。

          9.1 瀏覽數(shù)據(jù)

          可以打開數(shù)據(jù)顯示界面的地方有多個,包括打開視圖、打開表、執(zhí)行查詢,視圖在本章中使用一個ViewData對象來表示,表使用一個TableData對象表示,但是這些查詢都是共一個界面,因此我們新建一個QueryObject的接口,讓ViewData(視圖對象)和TableData去實現(xiàn)這個接口,該接口定義如下方法。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectQueryObject.java

          public interface QueryObject {

                   //得到數(shù)據(jù)

                   ResultSet getDatas(String orderString);

                   //得到查詢的名稱

                   String getQueryName();

                   //返回查詢的SQL

                   String getQuerySQL(String orderString);  

          }

          除了ViewData與TableData外,當執(zhí)行一些SQL語句的時候,也需要將數(shù)據(jù)顯示到DataFrame中,因此還需要建立一個QueryData的對象,表示一次查詢所顯示的數(shù)據(jù)對象,同樣去實現(xiàn)QueryObject接口。QueryObject接口只需要實現(xiàn)得到數(shù)據(jù)的方法,在ViewData和TableData,只需要使用SELECT語句就可以從視圖和表中得到相應的ResultSet對象。在本章中,ViewData和TableData都保存一個Database對象,因此可以直接使用JDBC去執(zhí)行,但是新建的QueryData對象就不屬于任何的數(shù)據(jù)庫,因此需要為該類提供構造器。

          代碼清單:codemysql-managersrcorgcrazyitmysqlobjectlistQueryData.java

                   //此次查詢的SQL語句

                   private String sql;

                   //對應的數(shù)據(jù)庫對象

                   private Database database;

                   public QueryData(Database database, String sql) {

                             this.sql = sql;

                             this.database = database;

                   }

          編寫完ViewData、TableData和QueryData對接口QueryObject的方法后,就可以將數(shù)據(jù)顯示到DataFrame中。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiDataFrame.java

                   public DataFrame(QueryObject queryObject) {

                             this.queryObject = queryObject;

                             //省略其他代碼

                   }

          為DataFrame提供一個構造器,參數(shù)是接口QueryObject,那么DataFrame就可以不用關心數(shù)據(jù)來源,只需要負責數(shù)據(jù)顯示就可以了,這些數(shù)據(jù)來源只封裝在一個QueryObject中,有可能是表的數(shù)據(jù),也有可能是視圖的數(shù)據(jù)。DataFrame中處理數(shù)據(jù)的時候需要注意的是,JTable的列也是動態(tài)的,它的列數(shù)由ResultSet來決定的,得到一個ResultSet的列數(shù),可以使用ResultSetMetaData對象的getColumnCount方法來得到列數(shù)。

          本章使用一個DataTable對象表示一個數(shù)據(jù)列表,該對象繼承JTable,在2.3中已經(jīng)創(chuàng)建。新建一個DataColumn對象,表示一個數(shù)據(jù)列,新建一個DataCell對象,表示一個數(shù)據(jù)單元格對象。

          DataColumn包括如下屬性。

                   //該列在JTable中的索引

                   private int index;

                   //該列的名稱

                   private String text;

          DataCell包括如下屬性:

                   //該單元格所在的行

                   private int row;

                   //該單元格所在的列

                   private DataColumn column;

                   //該單元格的值

                   private String value;

          另外,需要為DataCell對象重寫toString方法,返回該對象的value值。那么我們可以使用以下代碼來創(chuàng)建列表的數(shù)據(jù)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiDataFrame.java

                   while (this.rs.next()) {

                             DataCell[] data = new DataCell[columnCount];

                             //遍歷列, 創(chuàng)建每一個單元格對象

                             for (int j = 0; j < columnCount; j++) {

                                      //得到具體的某一列

                                      DataColumn column = this.columns.get(j);

                                      //創(chuàng)建單元格對象

                                      DataCell dc = new DataCell(i, column,

                                                         this.rs.getString(column.getText()));

                                      data[j] = dc;

                             }

                             datas.add(data);

                   }

          遍歷一個ResultSet對象,遍歷所有的列,最后構造一個DataCell對象,并存放到結果的集合中,注意結果集合是一個List對象,List對象里面的元素是DataCell數(shù)組。最后在設置列表數(shù)據(jù)時候,可以DefaultTableModel的setDataVector方法將數(shù)據(jù)與列的信息設置到列表中。這里需要注意的是,不管以什么方式進入數(shù)據(jù)顯示界面,都需要重新創(chuàng)建一個DataFrame對象。

          9.2 刷新數(shù)據(jù)

          在DataFrame中,已經(jīng)保存有一個QueryObject的對象,如果需要對界面中的數(shù)據(jù)進行刷新,只需要重新讀取一次數(shù)據(jù),即執(zhí)行QueryObject的getDatas方法,再重新將數(shù)據(jù)設置到列表中即可。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiDataFrame.java

                   //刷新數(shù)據(jù)

                   private void refresh() {

                             //更新數(shù)據(jù)

                             this.rs = this.queryObject.getDatas(this.orderString);

                             //得到全部列

                             this.columns = getColumns(this.rs);

                             //設置數(shù)據(jù)到列表中

                             this.model.setDataVector(getDatas(), this.columns.toArray());

                             //設置每一列的寬

                             setTableColumn(this.table);

                   }

          除了刷新數(shù)據(jù)外,還有降序與升序的功能,當用戶對數(shù)據(jù)進行了降序或者升序操作時,就可以調(diào)用refresh方法對列表進行刷新。

          9.3 數(shù)據(jù)排序

          我們在2.3中,已經(jīng)實現(xiàn)了DataTable的整列選擇(具體請看2.3章節(jié)),實現(xiàn)整列選擇的時候,DataTable中保存一個selectColumn的值,表示用戶當前所選擇的列索引,當用戶選擇了整列的時候,selectColumn就等于具體的某列索引,因此就可以得到這一列的名稱(數(shù)據(jù)庫的字段名),然后再使用SQL中的ORDER BY語句對數(shù)據(jù)進行排序。

          代碼清單:codemysql-managersrcorgcrazyitmysqlui ableDataTable.java

                   //返回列的名稱

                   private String getSelectColumnIdentifier() {

                             //得到選中列索引

                             int selectIndex = this.table.getSelectColumn();

                             if (selectIndex == -1) return null;

                             DefaultTableColumnModel colModel = (DefaultTableColumnModel)this.table.getColumnModel();

                             return (String)colModel.getColumn(selectIndex).getIdentifier();

                   }

                   //降序

                   private void desc() {

                             //得到字段名稱

                             String column = getSelectColumnIdentifier();

                             //沒有選中整列, 不排序

                             if (column == null) return;

                             this.orderString = column + " " + MySQLUtil.DESC;

                             //刷新列表

                             refresh();

                   }

          以上代碼實現(xiàn)降序。在接口QueryObject的getDatas方法中,需要提供參數(shù)orderString,即排序語句,因此只需要重新執(zhí)行QueryObject的getDatas方法即可得到排序后的數(shù)據(jù)。升序的實現(xiàn)與降序一致,只是使用SQL的ORDER BY ASC即可實現(xiàn)。

          10 執(zhí)行SQL

          執(zhí)行SQL的界面已經(jīng)在2.8中實現(xiàn),只是一個簡單的文本域讓用戶輸入SQL,然后提供一個執(zhí)行SQL與保存SQL的功能,由于我們已經(jīng)實現(xiàn)了數(shù)據(jù)瀏覽的顯示,因此實現(xiàn)起來十分簡單。執(zhí)行SQL的界面在本章中為QueryFrame類。

          10.1 運行SQL

          在2.8中,用戶只需要輸入相關的SQL語句,就可以執(zhí)行該SQL語句,如2.8中的圖l0所示。如果用戶輸入的是INSERT、UPDATE等語句,那么就可以將執(zhí)行結果直接使用普通的提示框顯示出來,如果用戶輸入的是SELECT(查詢語句)、CALL(調(diào)用存儲過程或者函數(shù))語句,那么就需要將查詢封裝成一個QueryData對象(9.1中創(chuàng)建),再將QueryData對象作為構造參數(shù)傳遞給DataFrame,QueryData是QueryObject接口的其中一個實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqluiQueryFrame.java

                   //查詢

                   private void query() {

                             //得到SQL

                             String sql = this.editArea.getText();

                             try {

                                      //封裝一個QueryData對象

                                      QueryData queryData = new QueryData(this.database, sql);

                                      //顯示DataFrame

                                      DataFrame dataFrame =new DataFrame(queryData);

                                      dataFrame.setVisible(true);

                             } catch (Exception e) {

                                      e.printStackTrace();

                                      showMessage(e.getMessage(), "錯誤");

                             }

                   }

          以上的query方法,只有當用戶輸入有SELECT或者CALL關鍵字的時候才調(diào)用,其他情況則直接彈出執(zhí)行結果的提示(成功與否)。

          10.2 保存SQL

          保存SQL,只是將用戶輸入的SQL語句保存到一份文件中即可。從界面得到用戶輸入的數(shù)據(jù),然后提供一個文件選擇器讓用戶選擇,最后使用IO流將內(nèi)容輸入到文件就可以實現(xiàn)。

          代碼清單:codemysql-managersrcorgcrazyitmysqlutilFileUtil.java

                   //創(chuàng)建文件并將content寫到File中

                   public static void writeToFile(File file, String content) {

                             //創(chuàng)建新文件

                             createNewFile(file);

                             //寫入文件

                             FileWriter writer = new FileWriter(file);

                             writer.write(content);

                             writer.close();

                   }

          代碼清單:codemysql-managersrcorgcrazyitmysqluiQueryFrame.java

                   //寫入文件

                   public void writeToFile(File file) {

                             String content = this.editArea.getText();

                             //將內(nèi)容寫到文件中

                             FileUtil.writeToFile(file, content);

                   }

          執(zhí)行SQL的功能已經(jīng)完成,這是用戶輸入SQL再運行的一種操作形式,用戶還有另外一種操作形式,就是通過導入文件來執(zhí)行一份SQL文件,下一小節(jié)將講解如何實現(xiàn)SQL文件的導入與導出。

          11 SQL文件的導入與導出

          SQL文件的導入與導出,包括數(shù)據(jù)庫的導入與導出、表的導出。當用戶選擇了某個數(shù)據(jù)庫的時候,就提供鼠標右鍵菜單,讓用戶可以執(zhí)行數(shù)據(jù)庫的導出與導入操作。當用戶選擇了某個表的時候,就可以提供鼠標右鍵菜單,提供導出功能。右鍵菜單已經(jīng)在2.9與2.10中實現(xiàn)。

          11.1 執(zhí)行SQL文件

          進入管理器的第一個界面,就需要用戶選擇MySQL的安裝目錄,根據(jù)用戶所選擇的目錄,我們就可以找到MySQL安裝目錄下面的bin目錄,然后找到mysqldump與mysql這兩個命令。mysqldump命令主要用于導出SQL文件,mysql命令主要用于執(zhí)行SQL文件。

          在3小節(jié)中,我們實現(xiàn)了用戶選擇MySQL安裝目錄的功能,并且將用戶選擇的目錄存放到一個GlobalContext的對象中,那么如果需要使用mysql命令執(zhí)行SQL文件,直接拼裝命令執(zhí)行即可。至于執(zhí)行的方式,將在下面講解。

          例如MySQL中存在一個數(shù)據(jù)庫,如果需要執(zhí)行某份SQL文件,就需要執(zhí)行以下語句:

          "MySQL安裝目錄inmysql" –u用戶名 –p密碼 –h連接IP –D數(shù)據(jù)庫名稱 < "SQL文件絕對路徑"

          使用mysql命令執(zhí)行SQL文件,mysql命令有許多的參數(shù),以上語使用了-u、-p、-h和-D參數(shù),-u參數(shù)表示數(shù)據(jù)庫的用戶名,-p表示密碼,-h表示連接的服務器IP,-D表示需要執(zhí)行文件的數(shù)據(jù)庫,拼裝好以上的語句后,可以使用Runtime類的exec方法執(zhí)行。

          注意:這里需要特別說明的是,如果MySQL安裝目錄有空格,或者SQL文件的絕對路徑有空格,首先需要將mysql命令(包括安裝目錄)使用雙引號括起來,再將SQL文件的絕對路徑使用雙引號括起來,但是直接使用Runtime的exec執(zhí)行仍然會出錯,我們可以將拼裝的那句命令,使用IO流寫入到一份.bat文件中,然后使用Runtime的exec方法執(zhí)行:“cmd /c bat文件所在”可消除錯誤。當然,這是在Windows操作系統(tǒng)下,如果使用Linux的話,可以生成sh文件。

          11.2 導出數(shù)據(jù)庫與表

          導出數(shù)據(jù)庫與執(zhí)行SQL文件一樣,使用mysqldump命令即可實現(xiàn)。mysqldump語句格式如下:

          "MySQL安裝目錄inmysqldump" -u用戶名 -p密碼 -h連接IP --force --databases 數(shù)據(jù)庫名 > "導出的SQL保存目錄"

          以上使用了mysqldump集合的-u、-p、-h、--force和—databases參數(shù),-u、-p和-h分別代表用戶名、密碼和數(shù)據(jù)庫服務器IP,--force參數(shù)表示發(fā)生錯誤將繼續(xù)執(zhí)行,--database參數(shù)表示需要導出的數(shù)據(jù)庫名稱。導出SQL文件與執(zhí)行SQL文件一樣,都是將拼裝的命令使用IO流寫到一份bat或者sh文件中,再使用Runtime的exec方法執(zhí)行。

          導出表與導出數(shù)據(jù)庫實現(xiàn)一致,只需要在導出數(shù)據(jù)庫的參數(shù)的基礎上,再加上—tables參數(shù)來聲明需要導出的表即可。多個表之間使用空格將表名分隔。導出表使用的mysqldump語句格式如下:

          "MySQL安裝目錄inmysqldump" -u用戶名 -p密碼 -h連接IP --databases 數(shù)據(jù)庫名稱 --tables 表一 表N  > "導出的SQL文件保存目錄"

          在本章中,處理SQL文件的導入與導出由BackupHandler接口完成,該接口有一個BackupHandlerImpl的實現(xiàn)類,已經(jīng)對SQL文件的導出和導出進行實現(xiàn),這些實現(xiàn)只是拼裝一些語句,真正執(zhí)行這些語句由CommandUtil中的executeCommand方法執(zhí)行,該方法提供了Windows下的實現(xiàn)(生成一份bat文件并執(zhí)行、最后刪除)。

          12 本章小節(jié)

          本章實現(xiàn)了一個MySQL管理器,這個管理器中有多個功能,包括數(shù)據(jù)庫元素的管理、數(shù)據(jù)瀏覽與SQL文件的導出和導出。我們可以在管理器實現(xiàn)的過程中,了解MySQL管理器的實現(xiàn)原理。實現(xiàn)MySQL管理器功能并不困難,困難的是一些界面的交互,特別是表管理界面。本章的這個MySQL管理器與一些流行的管理器有著部分的區(qū)別,例如我們在進入管理器的時候,需要用戶選擇MySQL的安裝目錄,目的為了使用MySQL的一些內(nèi)置命令,但是例如平常使用的Navicat,它并不需要知道MySQL的安裝目錄(或者根本不需要安裝MySQL),使用程序來導出SQL文件。當用戶需要導出SQL文件的時候,我們也可以使用程序來對數(shù)據(jù)庫進行分析,再寫到SQL文件中,這樣也是一種實現(xiàn)途徑。本章的目的并理器競爭不是與這些商業(yè)管,而是讓讀者了解管理器實現(xiàn)的原理。希望大家能在本章的基礎上,開發(fā)出功能更加強大的MySQL管理器。



          汽車檢測軟件濾波汽車維修管理軟件介紹
          汽車4s管理軟件怎么選如何利用汽車維修管理軟件完善汽修廠前臺接待規(guī)范
          汽車美容管理軟件功能分析汽車會員積分管理軟件
          汽車租賃軟件汽車整車構造虛擬教學軟件
          汽修/汽配/汽車美容軟件汽車快修服務軟件
          汽車4S店管理軟件汽車構造仿真教學軟件
          MySQL個人學習筆記MySQL排序語句
          MySQL入門篇MySQL緩存技術
          信息發(fā)布:廣州名易軟件有限公司 http://m.jetlc.com
          • 勁爆價:
            不限功能
            不限用戶
            1998元/年

          • 微信客服

            <output id="r87xx"></output>
          1. 
            
            <mark id="r87xx"><thead id="r87xx"><input id="r87xx"></input></thead></mark>
              • 欧美精品久久久久久 | 自拍偷拍综合网 | 午夜精品成人无码 | 免费在线观看网站性情淫乱做爱 | 国产无码日韩 | 日本日逼网站 | 熟女视频大香蕉视频大香蕉视频大香蕉 | 国产 在线 | 日韩 | 麻豆成人影院 | 中文字幕无码影院 |