JavaでのFirebirdのUI

エントリー



1年前、学期末レポートの一部としてデータベースを作成する必要がありました。 それは多くの労働を引き起こしませんでした。 私はトピックを選択し、ER図を描き、テーブルのフィールドを決定し、書き始めました。 私は長い間言語を選択していませんでしたが、その時点でEclipseでJavaの作業を始めました。 DBMSを選択しましたが、Firebirdを選択しました。 IBExpertを使用してテーブルを追加し、いくつかのテーブルのUIを作成するとすぐに、コピーアンドペーストを使用して残りのテーブルを作成できることに気付きました。 コードはひどいことがわかりました(OOPが聞こえなかったのですか?そのように説明できました)が、その瞬間、すべてが私を喜ばせました。 1年が経ち、偶然あなたのコードを修正しなければなりませんでした。 分かりにくい構造でひどいものでした。



私はいくつかの目標を設定することにしました:

-簡単にテーブルを追加

-最後にOOPを適用します

-設計パターンの適用(トレーニング用)



研究所の人々が単純なデータベース(または怠iness)を書くことがなぜ難しいのかは今も明らかではありませんが、いずれにせよ、データベースを書くことのシンプルさを示し、アプリケーションの私のビジョンを紹介したいと思います(私の意見では非常に簡単です)。



はじめに



データベースを作成するには、次のものが必要です。

-Java開発者向けのEclipse IDE

-火の鳥

-Jaybird(JDBCドライバー、基本的にjarライブラリー)

-IBExpert(テーブルの追加用)



これらはすべて簡単にダウンロードできるので、環境をセットアップするという質問はスキップします。 この記事では、単一のテーブルにインターフェースを実装します。 他のテーブルは簡単に追加できます。



コード記述



ID列とRANK列のあるランクテーブルは1つだけです。

画像



インターフェースを記述するためにSwingを選択しました。

インターフェイスの責任は

-テーブルの選択

-出力\テーブルの更新

-レコードの追加\削除\挿入

その結果、このインターフェイスができます



次のようにアプリケーションアーキテクチャを表します

-メイン(..)を持つメインクラス(アプリケーション)

-データベースへの接続(DBHelper)

-任意のテーブルの一般的なモデル(AbsTable、Tables)

-すべてのテーブルの基本クラス(BaseFrame)および継承テーブルのクラス(RankFrame)

-コンポーネント(コンポーネント)を作成するクラス

-アンドロイド(文字列)に触発された文字列のヘルパークラス

テーブルモデルの作成


データのタイプは明らかにID-整数およびランク-文字列です。 列の名前は明らかです。

このデータをクラステーブルにデータを入れます。 特定のテンプレートに従って、新しく作成されたすべてのテーブルもここで説明する必要があります。

public class Tables { public static final Class<?>[] RANKS_TYPE = { Integer.class, String.class }; public static final String[] RANKS_TABLE = { "ID", "Rank" }; }
      
      





また、テーブル内のデータのモデルを実装するAbsTableクラス(AbstractTableModelから継承)を作成します;基本クラスのいくつかのメソッドをオーバーライドする必要があります。 実装は簡単で、Tablesクラスからデータを取得してデータマトリックスを作成します。 そのため、一般的な方法でテーブルをモデル化し、各テーブルのコードの無駄なコピーを回避できます。

 public class AbsTable extends AbstractTableModel { private List<String> mColumnNames; private List<ArrayList<Object>> mTableData; private List<Object> mColumnTypes; public AbsTable(Class<?>[] types, String[] columns) { mColumnTypes = new ArrayList<Object>(types.length); mColumnNames = new ArrayList<String>(columns.length); for (int i = 0; i < columns.length; ++i) { mColumnTypes.add(i, types[i]); mColumnNames.add(columns[i]); } } @Override public int getColumnCount() { return mColumnNames.size(); } @Override public int getRowCount() { return mTableData.size(); } @Override public Object getValueAt(int row, int column) { return mTableData.get(row).get(column); } public String getColumnName(int column) { return mColumnNames.get(column); } @Override public boolean isCellEditable(int row, int column) { return false; } @Override public void setValueAt(Object obj, int row, int column) { } @Override public Class<?> getColumnClass(int col) { return (Class<?>) mColumnTypes.get(col); } public void setTableData(ArrayList<ArrayList<Object>> tableData) { mTableData = tableData; } }
      
      





DB接続


接続は1つなので、クラスはシングルトンを使用して実行されます。 接続を作成するには、getInstance()メソッドを使用します。このメソッドは、connect()メソッドを使用して、指定されたログイン/パスワード/ FDBファイルへのパスでデータベースに接続します。 このモデルでは、getData(String sql)メソッドを使用してデータを取得します。 また、接続が不要になったら、それを閉じる必要があります。そのためには、release()メソッドを使用します。

 public class DBHelper { private Connection dbConnection; private static DBHelper sDBHelper ; private static final String DRIVER = "org.firebirdsql.jdbc.FBDriver"; private static final String URL = "jdbc:firebirdsql:localhost/3050:C:\\DB\\DB.FDB"; private static final String LOGIN = "SYSDBA"; private static final String PASSWORD = "masterkey"; public static synchronized DBHelper getInstance() { if (sDBHelper == null) { sDBHelper = new DBHelper (); } return sDBHelper ; } private DBHelper () { } public void connect() { try { Class.forName(DRIVER); dbConnection = DriverManager.getConnection(URL, LOGIN, PASSWORD); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public PreparedStatement getPrepareStatement(String sql) throws SQLException { return dbConnection.prepareStatement(sql); } public synchronized ArrayList<ArrayList<Object>> getData(String query) { ArrayList<ArrayList<Object>> dataVector = new ArrayList<ArrayList<Object>>(); Statement st = null; ResultSet rs = null; try { st = dbConnection.createStatement(); rs = st.executeQuery(query); int columns = rs.getMetaData().getColumnCount(); while (rs.next()) { ArrayList<Object> nextRow = new ArrayList<Object>(columns); for (int i = 1; i <= columns; i++) { nextRow.add(rs.getObject(i)); } dataVector.add(nextRow); } } catch (SQLException e) { e.printStackTrace(); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (st != null) { try { st.close(); } catch (SQLException e) { e.printStackTrace(); } } } return dataVector; } public void release() { if (sDBHelper != null) { sDBHelper = null; } if (dbConnection != null) { try { dbConnection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
      
      





コンポーネント作成


JTable、JScrollPane、JComboBox、JLabel、JTextField、JButtonなどのコンポーネントが必要です。 クラスは、テーブルを作成するときにコンポーネント作成コードの無駄なコピーを避けるために作成されます。

 public class Components { public static AbsTable createTableModel(Class<?>[] types, String[] col, String sql) { AbsTable table = new AbsTable(types, col); table.setTableData(DBHelper .getInstance().getData(sql)); return table; } public static JTable createTable(AbsTable model) { JTable table = new JTable(model); table.getColumnModel().getColumn(0).setMaxWidth(50); return table; } public static JScrollPane createScroll(JTable table) { return new JScrollPane(table); } public static JComboBox<String> createCombo(String[] items, ItemListener listener) { JComboBox<String> combo = new JComboBox<String>(items); combo.setEditable(false); combo.setSelectedIndex(-1); combo.addItemListener(listener); return combo; } public static JLabel createLabel(String name) { JLabel label = new JLabel(name); label.setHorizontalTextPosition(JLabel.LEFT); label.setIconTextGap(5); label.setForeground(Color.black); return label; } public static JTextField createEdit(String text) { JTextField tf= new JTextField(text); tf.setEditable(true); tf.setForeground(Color.black); return tf; } public static JButton createButton(String name, ActionListener listener) { JButton button = new JButton(name); button.addActionListener(listener); return button; } }
      
      





フレームとテーブルインターフェイスへのテーブルの出力


インターフェースの責任はテーブルの出力であり、テーブルを追加する\更新する\削除する\変更することはすでに決定しています。 したがって、JFrameから継承した基本抽象クラスBaseFrameを作成します。

責任に基づいて、レコードの追加/削除/変更を確実にするためにすべてのテーブルを更新する必要があります。したがって、すべてのテーブルにadd()、delete()、save()、updateTableメソッドを追加します。 また、基本クラスには接続へのリンクが必要です(DBHelperがあります)。

フレーム上にあります

-テーブルJTable

-JButtonボタンは、エントリを追加\保存\削除します

-多数のレコードのJScrollPaneスクロール

これは作成されたすべてのテーブルで同じであるため、基本クラスにあります。 Componentsクラスからボタンを作成し、アクションリスナーを割り当てます。 また、ListSelectionListenerインターフェイスを使用して基本クラスを拡張し、テーブルのセルをクリックするイベントをキャッチします。

 abstract class BaseFrame extends JFrame implements ListSelectionListener { protected JButton mDeleteBtn; protected JButton mAddBtn; protected JButton mSaveBtn; protected JPanel mControlArea; protected JPanel mEditArea; protected JScrollPane mScroll; protected JTable mTable; protected Container mContainer; protected AbsTable mTableModel; protected DBHelper sDBHelper ; private static final int SIZE_X = 300; private static final int SIZE_Y = 450; public BaseFrame(String name) { super(name); sDBHelper = DBHelper .getInstance(); sBDHelper.connect(); mAddBtn = Components.createButton(Strings.ADD, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { add(); } }); mDeleteBtn = Components.createButton(Strings.DELETE, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { delete(); } }); mSaveBtn = Components.createButton(Strings.SAVE, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { save(); } }); setSize(new Dimension(SIZE_X, SIZE_Y)); setVisible(true); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } abstract void updateTable(); abstract void add(); abstract void delete(); abstract void save(); }
      
      





最後にテーブルを作成します。 編集するには、2つのJTextFieldと2つのJLabelが必要です。 Componentクラスを使用してコンポーネントを作成します。 フレームにコンポーネントを追加します。 次に、データベースを操作するために基本クラスの抽象メソッドをオーバーライドする必要があります(レコードの追加、変更、削除)。 これらのメソッドを作成するには、SQLの知識が少し必要です。 オーバーライドされたupdateTableメソッドでテーブルインターフェイスを更新することを忘れないでください。 また、valueChanged(..)メソッドを再定義して、セルのクリックを処理します。

 public class RanksFrame extends BaseFrame { private JLabel mIdLabel; private JLabel mRankLabel; private JTextField mIdEdit; private JTextField mRankEdit; public RanksFrame() { super(Strings.RANK); mContainer = getContentPane(); mTableModel = Components.createTableModel(Tables.RANKS_TYPE, Tables.RANKS_TABLE, "SELECT * FROM RANKS ORDER BY ID"); mTable = Components.createTable(mTableModel); mScroll = Components.createScroll(mTable); mIdLabel = Components.createLabel(Strings.ID); mIdEdit = Components.createEdit(""); mRankLabel = Components.createLabel(Strings.RANK); mRankEdit = Components.createEdit(""); mTable.getSelectionModel().addListSelectionListener(this); mControlArea = new JPanel(new GridLayout(1, 3)); mEditArea = new JPanel(new GridLayout(2, 2)); mEditArea.add(mIdLabel); mEditArea.add(mIdEdit); mEditArea.add(mRankLabel); mEditArea.add(mRankEdit); mControlArea.add(mSaveBtn); mControlArea.add(mDeleteBtn); mControlArea.add(mAddBtn); mContainer.add(mScroll); mContainer.add(mEditArea); mContainer.add(mControlArea); mContainer.setLayout(new BoxLayout(mContainer, BoxLayout.Y_AXIS)); } @Override public void updateTable() { SwingUtilities.invokeLater(new Runnable() { public void run() { mTableModel.setTableData(sBDHelper .getData("SELECT * FROM RANKS ORDER BY ID")); mTable.updateUI(); mRankEdit.setText(null); mIdEdit.setText(null); } }); } @Override public void add() { PreparedStatement ps = null; try { ps = sBDHelper .getPrepareStatement("INSERT INTO RANKS (ID,RANK) VALUES(?,?)"); ps.setString(1, mIdEdit.getText()); ps.setString(2, mRankEdit.getText()); ps.executeUpdate(); } catch (SQLException r) { r.printStackTrace(); } finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } updateTable(); } } @Override public void delete() { PreparedStatement ps = null; try { ps = sBDHelper.getPrepareStatement("DELETE FROM RANKS WHERE ID=?"); ps.setString(1, mIdEdit.getText()); ps.executeUpdate(); } catch (SQLException r) { r.printStackTrace(); } finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } updateTable(); } } @Override public void save() { PreparedStatement ps = null; try { ps = sBDHelper .getPrepareStatement("UPDATE RANKS SET RANK=? WHERE ID=?"); ps.setString(1, mRankEdit.getText()); ps.setString(2, mIdEdit.getText()); ps.executeUpdate(); } catch (SQLException r) { r.printStackTrace(); } finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } updateTable(); } } @Override public void valueChanged(ListSelectionEvent e) { mIdEdit.setText(mTable.getModel() .getValueAt(mTable.getSelectedRow(), 0).toString()); mRankEdit.setText(mTable.getModel() .getValueAt(mTable.getSelectedRow(), 1).toString()); } }
      
      





終了間近、テーブル選択インターフェイス


メインデータベースウィンドウのコードを記述します。 選択インターフェイスは、mTablesテーブル名を持つJComboBoxのように見えます。 選択したテーブルへのスイッチを押すと、すべてのテーブルを追加してそれらを作成する必要があります。 ああ、最後に、アプリケーションのクローズを処理するために、WindowListenerインターフェイスを使用します(使用するメソッドのみを記述し、コードが非常に多いため残りをスローしました)。閉じるときに、接続を閉じます。

 public class Application extends JFrame implements WindowListener { private String[] mTables = { "Ranks" }; private static final int RANKS = 0; private JComboBox<String> mComboMenu; public Application() throws SQLException { super(Strings.DB_NAME); mComboMenu = Components.createCombo(mTables, new ItemListener() { @Override public void itemStateChanged(ItemEvent evt) { switch (mComboMenu.getSelectedIndex()) { case RANKS: new RanksFrame(); break; } SwingUtilities.invokeLater(new Runnable() { public void run() { mComboMenu.setSelectedIndex(-1); } }); } }); Container container = getContentPane(); container.add(mComboMenu); container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 80); setResizable(false); setVisible(true); addWindowListener(this); } public static void main(String[] args) throws SQLException { new Application(); } @Override public void windowClosing(WindowEvent arg0) { DBHelper .getInstance().release(); } }
      
      





おわりに



疲れていないことを願っています。この記事は本質的に教育的なものなので、この記事を読んだ後にデータベーステーブルを作成/作成するのが簡単になることを願っています。 また、学生が最終的に座って自分でデータベースを作成するという幻想的な希望に悩まされています。



PSリファクタリングが成功し、すべてがシンプルで明確に見えることを願っています。 特にデザインパターンでは、批判を歓迎します。

upd:

Vector-> ArrayList thanks javax

多くの欠点については、gvsmirnovとaleksandyに感謝します



All Articles