SQLの窓

2014年05月22日


Eclipse+WindowBuilder : DBアプリケーション(社員マスタメンテ)の更新(データ修正)処理

入力チェックが正しく完了した後に、画面データから UPDATE 構文によって SQL の 更新処理を作成して実行します。UPDATE 構文で更新される行は 1 行のみであり、主キーでその行が限定されます

社員マスタ
列名 型名 最大桁 主キー 型説明
1 社員コード VARCHAR 4 1 Null で終了する Unicode 文字列
2 氏名 VARCHAR 50   Null で終了する Unicode 文字列
3 フリガナ VARCHAR 50   Null で終了する Unicode 文字列
4 所属 VARCHAR 4   Null で終了する Unicode 文字列
5 性別 INT     4 バイトの符号付き整数
6 作成日 DATETIME     日付値
7 更新日 DATETIME     日付値
8 給与 INT     4 バイトの符号付き整数
9 手当 INT     4 バイトの符号付き整数
10 管理者 VARCHAR 4   Null で終了する Unicode 文字列
11 生年月日 DATETIME     日付値
更新処理を実行する為に入力をチェックを行っていますが、かならず更新前には操作しているユーザに確認を求める必要があります。それがなければ、意図せぬ更新処理作業を行ってしまう事が十分に考えられます。更新処理はシステムによって重大な処理であり、入力者の不注意で問題が起きるような可能性は最大限排除する必要があります。
			// ここに更新処理を記述する
			int result = JOptionPane.showConfirmDialog(contentPane, "更新してよろしいですか?", "更新確認", JOptionPane.OK_CANCEL_OPTION);
			if ( result == JOptionPane.CANCEL_OPTION) {
				rdbms.close();
				return;
			}

			try {
				
				String scode = syainCode.getText();
				String sname = syainName.getText();
				String ssyozoku = syainSyozoku.getText();
				int ssex = syainSex.getSelectedIndex();
				String skyuyo = syainKyuyo.getText();
				String steate = syainTeate.getText();
				String skanri = syainKanri.getText();
				String sbirth = syainBirth.getText();
						
				String sql = "";
				sql += "update 社員マスタ set";
				sql += " 氏名 = '" + sname + "',";
				sql += " 所属 = '" + ssyozoku + "',";
				sql += " 性別 = " + ssex + ",";
				sql += " 給与 = " + skyuyo + ",";
				if ( steate.trim().equals("") ) {
					sql += " 手当 = NULL,";
				}
				else {
					sql += " 手当 = " + steate + ",";
				}
				if ( skanri.trim().equals("") ) {
					sql += " 管理者 = NULL,";
				}
				else {
					sql += " 管理者 = '" + skanri + "',";
				}
				if ( sbirth.trim().equals("") ) {
					sql += " 生年月日 = NULL,";
				}
				else {
					sql += " 生年月日 = '" + sbirth + "',";
				}
				sql += " 更新日 = now()";
				sql += " where 社員コード = '" + scode + "'";
				System.out.println(sql);
				result = rdbms.stmt.executeUpdate(sql);
				System.out.println("更新件数 : " + result);
				JOptionPane.showMessageDialog(contentPane, "更新処理が実行されました");

				passControl.clearDetailes();			
				passControl.pass1Enable();			
				
				
			} catch (Exception ex) {
				ex.printStackTrace();
			}
			
			
			rdbms.close();
このコード上に、データベースへの接続処理が見えていませんが、直前に他のテーブルの行データの参照の為、既に接続済みです。また、SQLを作成する為に、入力フィールドの状況をチェックしていますが、本来の更新前のチェックで、全ての問題点が排除されているという前提でのコードとなっています。( 例 : 入力フィールドに数値以外の値や漢字スペースは含まれていない )

ここでは、SQL 文を文字列連結で作成していますが、これでは後からのメンテナンス性が悪いので、ベースとなる一文としての SQL 文をテキストで外部に作成して、データ部分を入力値で置き換えるようなクラスまたはメソッドを作成する必要があります。

関連する記事


posted by lightbox at 2014-05-22 23:53 | Java | このブログの読者になる | 更新情報をチェックする

2014年05月17日


Eclipse+WindowBuilder : DBアプリケーション(社員マスタメンテ)の入力チェック

private class UpdateAction extends AbstractAction の全体ソース

入力チェックは、エラー処理も含めて『画面のあるアプリケーション』の仕様の中核となるものです(入力チェック・画面編集・更新処理が中核)

その中でも最も実装が多くなる事が予想されるのが入力チェックで、マスタメンテのような単純な一つの表に対する更新ではあまり他の関連性をチェックする事はありませんが、システムとして整合性を保つ為や、使用するユーザが迷う事無く作業を完了させるために必要な重要な機能となります。

ここでの処理のシナリオとしては、更新ボタンをクリックした直後に入力チェックを一つづつ行い、エラーがあると認識できたらメッセージボックスでエラー内容を表示し、そのエラーの起こったフィールドにフォーカスを移動して入力内容を全て選択状態にした後メソッドを抜けます。

全てのチェックを通り抜けないと更新処理は行いません。

社員マスタメンテで通常考えられる入力チェックは以下のようなものです( 実際は複合される可能性があります )
(1) 必須入力チェック
(2) 入力文字数チェック
(3) 数値チェック
(4) 日付チェック
(5) 他テーブル参照チェック
必須入力チェック
			// 必須チェック
			String inputString = null;
			inputString = syainName.getText();
			inputString = inputString.replaceAll(" ", "");	// 漢字スペースを除去
			if ( inputString.trim().equals("") ) {	// スペースを除去してなにも無い場合
				JOptionPane.showMessageDialog(
						contentPane,
						"必須入力です",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainName.setText("");	// 空白文字があっても無いようにする
				syainName.requestFocusInWindow();
				syainName.selectAll();
				return;
			}


入力文字数チェック
			// 文字数チェック
			int len = 0;
			len = (syainName.getText()).length();
			if ( len > 50 ) {
				JOptionPane.showMessageDialog(
						contentPane,
						"文字数が長すぎます",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainName.requestFocusInWindow();
				syainName.selectAll();
				return;
			}


数値チェック
			// 数値チェック
			int num = 0;
			try {
				num = Integer.parseInt(syainSyozoku.getText());
			} catch (Exception ex) {
				num = -1;
			}
			if ( num < 0 ) {
				JOptionPane.showMessageDialog(
						contentPane,
						"正しい数字を入力して下さい",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainSyozoku.requestFocusInWindow();
				syainSyozoku.selectAll();
				return;
			}


日付チェック
			// 日付チェック
			String birth = syainBirth.getText();
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
			dateFormat.setLenient(false);
			Date date = null;
			try {
				date = dateFormat.parse(birth);
			} catch (Exception ex) {
				date = null;
			}
			if ( date == null ) {
				JOptionPane.showMessageDialog(
						contentPane,
						"日付が正しくありません",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainBirth.requestFocusInWindow();
				syainBirth.selectAll();
				return;
			}


他テーブル参照チェック
			if ( !rdbms.getConnect() ) {
				JOptionPane.showMessageDialog(
						contentPane,
						"データベースに接続できませんでした\nシステム管理者に連絡してください",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainCode.requestFocusInWindow();
				syainCode.selectAll();
				return;
			}
			
			// 参照チェック
			try {
				
				rdbms.stmt = rdbms.con.createStatement();
				String scode = syainSyozoku.getText();
				String query = "";
				query += "select *";
				query += " from コード名称マスタ";
				query += " where コード名称マスタ.区分 = 2";
				query += " and コード  = '" + scode + "'";				
				System.out.println(query);
				rdbms.rset = rdbms.stmt.executeQuery ( query );
				if ( rdbms.rset.next() ) {
					syainSyozokuName.setText(rdbms.rset.getString( "名称" ));
				}
				else {
					JOptionPane.showMessageDialog(
							contentPane,
							"入力したコードは存在しませんでした",
							"エラー",
							JOptionPane.ERROR_MESSAGE);
					syainSyozoku.requestFocusInWindow();
					syainSyozoku.selectAll();
					rdbms.close();
					return;
				}
				
			} catch (Exception ex) {
				ex.printStackTrace();
			}		


関連する記事


posted by lightbox at 2014-05-17 14:09 | Java | このブログの読者になる | 更新情報をチェックする

Eclipse+WindowBuilder : DBアプリケーション(社員マスタメンテ)の二会話画面制御

アプリケーションにおける『会話数』は、ウインドウ数にあたり、2会話目を終了するか、キャンセルしない限り1会話目の結果を変更する事ができないというルールを守る事によって、矛盾の無いデータの処理を行う事が可能になります。

その為に必要な一般的な処理として、1会話目の画面状態の作成、1会話目の画面状態から2会話の画面状態への変更( 画面遷移 )、2会話目の画面に表示されたものを全て消去して初期化する、という3つの処理が必要になりますPassControl クラス
	private class PassControl {

		// 第一会話を使用可能にし、第二会話を使用不能にする
		// フォーカスコントロール
		public void pass1Enable() {
			
			syainCode.setEnabled(true);
			checkButton.setEnabled(true);

			syainName.setEnabled(false);
			syainFuri.setEnabled(false);
			syainSyozoku.setEnabled(false);
			syainSex.setEnabled(false);
			syainKyuyo.setEnabled(false);
			syainTeate.setEnabled(false);
			syainKanri.setEnabled(false);
			syainBirth.setEnabled(false);

			updateButton.setEnabled(false);
			cancelButton.setEnabled(false);
			
			syainCode.selectAll();
			syainCode.requestFocusInWindow();			
		}

		// 第二会話を使用可能にし、第一会話を使用不能にする
		// フォーカスコントロール
		public void pass2Enable() {

			syainCode.setEnabled(false);
			checkButton.setEnabled(false);

			syainName.setEnabled(true);
			syainFuri.setEnabled(true);
			syainSyozoku.setEnabled(true);
			syainSex.setEnabled(true);
			syainKyuyo.setEnabled(true);
			syainTeate.setEnabled(true);
			syainKanri.setEnabled(true);
			syainBirth.setEnabled(true);
			
			updateButton.setEnabled(true);
			cancelButton.setEnabled(true);

			syainName.selectAll();
			syainName.requestFocusInWindow();			
			
		}

		// 第二会話の入力項目をクリアする
		// 選択コントロール
		public void clearDetailes() {
			
			syainSyozokuName.setEditable(false);
			syainSyozokuName.setEnabled(true);
			syainSyozokuName.setFocusable(false);
			syainKanriName.setEditable(false);
			syainKanriName.setEnabled(true);
			syainKanriName.setFocusable(false);

			syainName.setText("");
			syainFuri.setText("");
			syainSyozoku.setText("");
			syainSyozokuName.setText("");
			// コンボボックス非選択
			syainSex.setSelectedIndex(-1);
			syainKyuyo.setText("");
			syainTeate.setText("");
			syainKanri.setText("");
			syainKanriName.setText("");
			syainBirth.setText("");
			
		}

	}

PassControl クラスは、機能をまとめたものであり、メソッドを実行する事が目的なので、private で作成します。( メソッドを3つ作成しても大差はありません )

Eclipse で private クラスを作成するには、手作業で書いたほうが早いですが、一般的なクラス作成のダイアログで『エンクロージング型』にチェックする事で対話的にブロックを作成できます。


ブロックのソース上の位置は、『アウトラインウインドウ』でドラッグして変更できます。

※ インスタンスは、private 変数として定義すると同時に作成しています

初期画面は、passControl.clearDetailes(); と passControl.pass1Enable(); で作成され、キャンセルボタンでも、passControl.clearDetailes(); と passControl.pass1Enable(); が実行されます。社員マスタの内容が表示された時にのみ、passControl.pass2Enable() が実行されます。

その他の画面コントロール

(1) Enter キーを TAB キーと同等に扱い、次のフィールドへの移動に使用する
		addWindowListener(new WindowAdapter() {
			
			@Override
			public void windowOpened(WindowEvent arg0) {
				MainWindow.this.setLocationRelativeTo(null);

				// 接続文字列を作成して、Rdbms のインスタンスを作成
				currentDir = System.getProperty("user.dir");
				String connectionString = "Provider=MSDASQL"
				+ ";Driver={Microsoft Access Driver (*.mdb)}"
				+ ";Dbq=" + currentDir + "\\data\\販売管理C.mdb" + ";";
				rdbms = new Rdbms( connectionString );
				System.out.println(connectionString);
				
				passControl.clearDetailes();
				passControl.pass1Enable();
				
				// Enter キーで次のフォーカスへ移動する
				KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
				Set<AWTKeyStroke> forwardKeys = new HashSet<AWTKeyStroke>(focusManager.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
				forwardKeys.add(KeyStroke.getAWTKeyStroke(KeyEvent.VK_ENTER, 0));
				focusManager.setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
			}
		});


(2) 表示用の入力しない JTextField は、setEnabled を使わない
			// 所属名 編集不可
			syainSyozokuName.setEditable(false);
			// 所属名 使用可
			syainSyozokuName.setEnabled(true);
			// 所属名 フォーカスなし
			syainSyozokuName.setFocusable(false);
			// 管理者名 編集不可
			syainKanriName.setEditable(false);
			// 管理者名 使用可
			syainKanriName.setEnabled(true);
			// 管理者名 フォーカスなし
			syainKanriName.setFocusable(false);

setEnabled を使うと、表示内容が見にくい(画面上のコードが disable で内容が見にくいのが解ると思います)ので、編集不可にしてフォーカスが移動しないようにしています。

また、タブオーダーを指定する事ができないので、ボタンの位置を変更しました。




関連する記事


posted by lightbox at 2014-05-17 01:33 | Java | このブログの読者になる | 更新情報をチェックする

2014年05月16日


Eclipse+WindowBuilder : DBアプリケーション(社員マスタメンテ)でDBからSELECT構文で行データを読み出す

Java の本来の getter setter を作成する方法とは少しずれますが、少なくとも Microsoft の言語では一般的に『プロパティ』が使用されます。Microsoft でも実際の中身はメソッド( getter と setter ) ではありますが、public で変数を定義する事によって、処理を一箇所に集約する事は初歩的なアプリケーション作成では有用です。

ここではまず、データベースに接続するクラスを作成し、そのクラスに public なフィールドを定義してデーベース処理を簡潔にしています。

Rdbms.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;


public class Rdbms {

	public Connection con = null;
	public Statement stmt = null;
	public ResultSet rset = null;
	
	private String connectionString = null;

	public Rdbms(String connectionString) {
		this.connectionString = connectionString;
	}

	public boolean getConnect() {
		
		boolean connect_result = true;
		String targetDbc = null;
		
		if ( connectionString.substring(0, 3).equals("jdbc") ) {
			targetDbc = "JDBC";
		}
		else {
			targetDbc = "ODBC";
		}

		// *********************************************
		// ODBC 用接続文字列サンプル
		// *********************************************
//		// SQLServer
//		connectionString = "Provider=MSDASQL"
//				+ ";Driver={SQL Server Native Client 11.0}"
//				+ ";SERVER=.\\sqlexpress" + ";DATABASE=lightbox"
//				+ ";UID=sa" + ";PWD=" + pass + ";";
//		// MySQL
//		connectionString = "Provider=MSDASQL"
//				+ ";Driver={MySQL ODBC 5.3 Unicode Driver}"
//				+ ";SERVER=localhost" + ";DATABASE=lightbox"
//				+ ";UID=root" + ";PWD=" + pass + ";Charset=cp932";
//		// MDB
//		connectionString = "Provider=MSDASQL"
//				+ ";Driver={Microsoft Access Driver (*.mdb)}"
//				+ ";Dbq=" + currentDir + "\\data\\販売管理C.mdb" + ";";
//		// Oracle
//		connectionString = "Provider=MSDASQL"
//				+ ";Driver={Oracle in XE}" + ";DBQ=localhost:1521/XE"
//				+ ";UID=lightbox" + ";PWD=" + pass + ";";
//		// PostgreSQL
//		connectionString = "Provider=MSDASQL"
//				+ ";Driver={PostgreSQL ODBC Driver(UNICODE)}"
//				+ ";Servername=localhost" + ";Port=5432"
//				+ ";Database=lightbox" + ";UID=postgres"
//				+ ";PWD=" + pass + ";";
		
		try {
			if (targetDbc.equals("ODBC")) {
				Properties prop = new Properties();
				prop.put("charSet", "MS932");
				con = DriverManager.getConnection("jdbc:odbc:"+connectionString, prop);
			}
			else {
				con = DriverManager.getConnection(connectionString);
				// *********************************************
				// JDBC 用接続文字列サンプル
				// *********************************************
				// MySQL
				// mysql-connector-java-5.1.30-bin.jar
//					con = DriverManager.getConnection("jdbc:mysql://localhost/lightbox?" +
//						"user=root&password="+pass);
				// Oracle
				// ojdbc6.jar
//					con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/xe","lightbox",pass);
				// SQLServer
				// sqljdbc4.jar
//					String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
//						"databaseName=lightbox;user=sa;password="+pass+";";
//					con = DriverManager.getConnection(connectionUrl);
				// PostgreSQL
				// postgresql-9.3-1100.jdbc4.jar
//					String connectionUrl = "jdbc:postgresql://localhost:5432/lightbox" +
//	 					"?user=postgres&password="+pass+"&charSet=utf-8;";
//					con = DriverManager.getConnection(connectionUrl);
				
			}
			
		} catch (Exception ex) {
			// TODO 
			ex.printStackTrace();
			connect_result = false;
		}
		
		return connect_result;
	}

	public void close() {
		try {
			
			if ( rset != null ) {
				rset.close();
				rset = null;
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
		try {
			
			if ( stmt != null ) {
				stmt.close();
				stmt = null;
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		try {
			
			if ( con != null ) {
				con.close();
				con = null;
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}
}

コメント部分は他の RDBMS や jdbc ドライバを使ったり、jdbc-odbc ブリッジを使ったりする場合のサンプルとなっており。主たる処理は、DB に接続して con にインスタンスをセットする事と、終了時の処理を close として集約しているところです

このクラスはアプリケーションの初期化時に、対象となる RDBMS の接続文字列を使ってインスタンスを作成します。( ここでは MS Access )
		addWindowListener(new WindowAdapter() {
			
			@Override
			public void windowOpened(WindowEvent arg0) {
				MainWindow.this.setLocationRelativeTo(null);

				// 接続文字列を作成して、Rdbms のインスタンスを作成
				currentDir = System.getProperty("user.dir");
				String connectionString = "Provider=MSDASQL"
				+ ";Driver={Microsoft Access Driver (*.mdb)}"
				+ ";Dbq=" + currentDir + "\\data\\販売管理C.mdb" + ";";
				rdbms = new Rdbms( connectionString );
				System.out.println(connectionString);
			}
		});

後は、DB アクセスの処理開始イベントで接続、読込み、接続解除の処理を行います。

但し、ここではまだアプリケーションとしてユーザとの会話をコントロールする為の処理は実装されておらず、ただ以下のように disable 状態のフィールドに内容が表示されるだけです


	private class CheckAction extends AbstractAction {
		public CheckAction() {
			putValue(NAME, "確認");
			putValue(SHORT_DESCRIPTION, "Some short description");
		}
		public void actionPerformed(ActionEvent e) {
			System.out.println("確認ボタンがクリックされました");
			
			if ( !rdbms.getConnect() ) {
				JOptionPane.showMessageDialog(
						contentPane,
						"データベースに接続できませんでした\nシステム管理者に連絡してください",
						"エラー",
						JOptionPane.ERROR_MESSAGE);
				syainCode.requestFocusInWindow();
				syainCode.selectAll();
				return;
			}
			
			try {
				
				rdbms.stmt = rdbms.con.createStatement();
				String scode = syainCode.getText();
				String query = "";
				query += "select 社員マスタ.*,";
				query += " Format(社員マスタ.生年月日,'yyyy/MM/dd') as 生年月日2,";
				query += " コード名称マスタ.名称,";
				query += " 管理者参照.氏名 as 管理者名";
				query += " from (社員マスタ left join コード名称マスタ on 社員マスタ.所属 = コード名称マスタ.コード)";
				query += " left join 社員マスタ 管理者参照 on 社員マスタ.管理者 = 管理者参照.社員コード";
				query += " where コード名称マスタ.区分 = 2";
				query += " and 社員マスタ.社員コード = '" + scode + "'";				
				System.out.println(query);
				rdbms.rset = rdbms.stmt.executeQuery ( query );
				if ( rdbms.rset.next() ) {
					syainName.setText(rdbms.rset.getString( "氏名" ));
					syainFuri.setText(rdbms.rset.getString( "フリガナ" ));
					syainSyozoku.setText(rdbms.rset.getString( "所属" ));
					syainSyozokuName.setText(rdbms.rset.getString( "名称" ));
					syainSex.setSelectedIndex(rdbms.rset.getInt("性別"));
					syainKyuyo.setText(rdbms.rset.getString( "給与" ));
					syainTeate.setText(rdbms.rset.getString( "手当" ));
					syainKanri.setText(rdbms.rset.getString( "管理者" ));
					syainKanriName.setText(rdbms.rset.getString( "管理者名" ));
					syainBirth.setText(rdbms.rset.getString( "生年月日2" ));
					
				}
				else {
					JOptionPane.showMessageDialog(
							contentPane,
							"入力したコードは存在しませんでした",
							"エラー",
							JOptionPane.ERROR_MESSAGE);
					syainCode.requestFocusInWindow();
					syainCode.selectAll();
					rdbms.close();
					return;
				}
				
			} catch (Exception ex) {
				ex.printStackTrace();
			}				
			
			rdbms.close();
		}
	}

データベースは、販売管理C.MDB です。以下からダウンロードできます


Eclipse のプロジェクト内に data フォルダを作成してその中に置いて、パッケージエクスプローラから F5 で再表示すると以下のようになるはずです。



SELECT 構文について

ここで使用している SELECT 構文は、left join を二回行って、二つの他の表と結合しています。そのうちの一つは『コード名称マスタ』という、比較的件数の少ないシステム上のコード定義を行っているテーブルです。このテーブルの主キーは、区分とコードと言う二つの列から成り立っており、社員マスタの所属と、区分2 の行のコード部分が結合されます。

もう一つの結合する表は社員マスタであり、このような結合は『自己結合』と呼ばれます。この場合は、社員マスタに対して別名を定義し、(ここでは 管理者参照 )、かつ全ての列に対してテーブル名の修飾を行って一意な列名として参照するようにしています。

日付の編集を行っている関数は、MS Access のものです。この部分は RDBMS 毎に記述が変わるので注意して下さい。
select 社員マスタ.*
	, Format(社員マスタ.生年月日,'yyyy/MM/dd') as 生年月日2
	, コード名称マスタ.名称
	, 管理者参照.氏名 as 管理者名 
 from 
	(社員マスタ left join コード名称マスタ on 社員マスタ.所属 = コード名称マスタ.コード)
	left join 社員マスタ 管理者参照 on 社員マスタ.管理者 = 管理者参照.社員コード 
 where コード名称マスタ.区分 = 2 
   and 社員マスタ.社員コード = '0006'

関連する記事


posted by lightbox at 2014-05-16 20:14 | Java | このブログの読者になる | 更新情報をチェックする

Eclipse+WindowBuilder : ボタンイベント(AbstractAction) の作成



ボタンコントールの右クリックで表示される、Add event handler や、Design 上でボタンをダブルクリックして作成されるイベントコードは、『匿名のインナー型』と呼ばれる無名の処理のブロックとなります。当然ソースの記述位置は、ホタンの定義の近くに自動的に作成されるので、アプリケーションの簡単な振る舞いを定義するには便利でメンテナンス性も良いですが、アプリケーションにとって重大で大きなアクションを定義するのは向いていません。処理記述が大きくなれば、当然コンストラクタの記述を圧迫してメンテナンス性が全く無くなってしまいます。

そういった場合は、本来 public なクラスを作成して別のソースコードとして管理するものですが、WindowBuilder では最終的にそうする為の予備コーディングとして『アクションの設定』というメニューがあります



これを行うと、別のソースコードでは無く private なクラスとしての AbstractAction を自動的に作成してくれます。これによって作成されたアクションは、後から呼び出しの変更先として自由に Design 画面から各コントロールのイベントとして選択する事ができるようになります。



但し、自動的に作成されるので変数名は変更したほうが良いです。プロパティウインドウから変更する事によって、ソース上で使われているその変数名が全て変更されます。さらに、変数名だけでは無くクラス名そのものも変更して、処理の意味を明確にしておくべきでしょう。



クラス名の変更は、パッケージエクスプローラの該当するクラスを選択して、F2 キーで名前変更用のダイアログが表示されます。そして、最後にボタンのテキストを変更する為に、ソースコードの putValue の中身を変更しておきます。



こうする事によって、比較的管理しやすいソースコードとしてイベント記述を書く事ができるようになります。



※ putValue で、SHORT_DESCRIPTION に対して文字列を設定すると、ヒントウインドウの内容がその内容になります。
	private class CheckAction extends AbstractAction {
		public CheckAction() {
			putValue(NAME, "確認");
			putValue(SHORT_DESCRIPTION, "Some short description");
		}
		public void actionPerformed(ActionEvent e) {
			System.out.println("確認ボタンがクリックされました");
		}
	}



関連する記事


posted by lightbox at 2014-05-16 15:25 | Java | このブログの読者になる | 更新情報をチェックする

2014年05月15日


Eclipse+WindowBuilder : データベースアプリケーション(社員マスタメンテ)の画面作成

Eclipse と WindowBuilder の機能を使ってできるだけ簡単にデータベースアプリケーションを作成します。『社員マスタメンテ』は、主キーが一つでシステム稼動の為に最初から必要な『マスターテーブル』の一つとして想定しています。本来は、『登録・修正・削除・一覧印刷』の機能を持つべきですが、ここでは一つのアプリケーションに一つの機能という事で簡潔にする為、『修正』のみに限定します。

データベースは、特別な環境を構築せずに使える MS Access( MDB ) を使用します。Java の接続を変更すれば、若干の SQL 変更で Oracle でも動作します。( 今回この記事では SQL を使用していませんが )
関連する記事

JDBC と ODBC を使った、オールマイティなデータベース接続サンプル
MDB で使用する社員マスタの CERATE 文は以下のようになります
create table [社員マスタ] (
	[社員コード] VARCHAR(4)
	,[氏名] VARCHAR(50)
	,[フリガナ] VARCHAR(50)
	,[所属] VARCHAR(4)
	,[性別] INT
	,[作成日] DATETIME
	,[更新日] DATETIME
	,[給与] INT
	,[手当] INT
	,[管理者] VARCHAR(4)
	,[生年月日] DATETIME
	,primary key([社員コード])
)

社員マスタを含んだ MDB ファイル(販売管理C.MDB)は以下からダウンロードできます


アプリケーション作成で最初に行うのは画面作成です。単純なレイアウトであれば、場合によっては、設計時に既に作成されている可能性もあります。ただ、ここではアプリケーションの基本機能として、いくつかの設定が必要になります。

その最も大きなものは『2会話処理』を前提とした、2会話目のコントロールの Disable 設定です。WindowBuilder では、Design のプロパティから設定できますが、結局はコードとなってコンストラクタで実行されます。

ですが、その前に JFrame を使った WindowBuilder のプロジェクトを作成する必要があります

WindowBuilder JFrame アプリケーションプロジェクト (1)

ワークスペースと Javaプロジェクト



何をするにも、ワークスペースと Javaプロジェクトは必要です。他からプロジェクトをインポートする場合にも、Eclipse 側でプロジェクトが必要です。


WindowBuilder JFrame アプリケーションプロジェクト (2)

JFrame クラスの作成と、JPanel の絶対レイアウト



JFrame の中に JPanel が作成され、JFrame がいわゆる Window の役目をします。JPanel は、コントロールのコンテナとして機能しますが、初期状態では『絶対レイアウト』にならないので、その設定までが基本操作になります


画面作成

以下のような画面を作成して、コードに入力した値に対応する社員のデータを更新する処理です。



コードと確認ボタンは第一会話グループで、初期状態として入力可能となります。それ以外は全て第二会話グループとなり、確認ボタンでエラーがなければ、第一会話グループを入力不可に変更して、第二会話グループの入力を可能にします。その際(第二会話グループへの移行直後)、初期フォーカスは氏名となります



第二会話グループを選択すると、共通となるプロパティが表示されるので、その中の enabled のチェックが外れた状態にします。

それ以外の調整も必要ですが、以下のソースコードをそのまま使用すると、WindowBuilder が Design に同じものを表示してくれます
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;


public class MainWindow extends JFrame {

	private JPanel contentPane;
	private JTextField syainCode;
	private JTextField syainName;
	private JTextField syainFuri;
	private JTextField syainSyozoku;
	private JTextField syainSyozokuName;
	private JComboBox<String> syainSex;
	private JTextField syainKyuyo;
	private JTextField syainTeate;
	private JTextField syainKanri;
	private JTextField syainKanriName;
	private JTextField syainBirth;
	private JButton checkButton;
	private JButton updateButton;
	private JButton cancelButton;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainWindow frame = new MainWindow();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public MainWindow() {
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowOpened(WindowEvent arg0) {
				MainWindow.this.setLocationRelativeTo(null);
			}
		});
		setResizable(false);
		setTitle("\u793E\u54E1\u30DE\u30B9\u30BF\u30E1\u30F3\u30C6");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 468, 331);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		syainCode = new JTextField();
		syainCode.setBounds(98, 29, 50, 19);
		contentPane.add(syainCode);
		syainCode.setColumns(10);
		
		syainName = new JTextField();
		syainName.setEnabled(false);
		syainName.setColumns(10);
		syainName.setBounds(98, 85, 202, 19);
		contentPane.add(syainName);
		
		syainFuri = new JTextField();
		syainFuri.setEnabled(false);
		syainFuri.setColumns(10);
		syainFuri.setBounds(98, 108, 202, 19);
		contentPane.add(syainFuri);
		
		syainSyozoku = new JTextField();
		syainSyozoku.setEnabled(false);
		syainSyozoku.setColumns(10);
		syainSyozoku.setBounds(98, 131, 42, 19);
		contentPane.add(syainSyozoku);
		
		syainSyozokuName = new JTextField();
		syainSyozokuName.setEditable(false);
		syainSyozokuName.setEnabled(false);
		syainSyozokuName.setColumns(10);
		syainSyozokuName.setBounds(153, 131, 147, 19);
		contentPane.add(syainSyozokuName);
		
		syainSex = new JComboBox<String>();
		syainSex.setEnabled(false);
		syainSex.setModel(new DefaultComboBoxModel<String>(new String[] {"\u7537\u6027", "\u5973\u6027"}));
		syainSex.setBounds(98, 154, 80, 19);
		contentPane.add(syainSex);
		
		syainKyuyo = new JTextField();
		syainKyuyo.setEnabled(false);
		syainKyuyo.setColumns(10);
		syainKyuyo.setBounds(98, 177, 96, 19);
		contentPane.add(syainKyuyo);
		
		syainTeate = new JTextField();
		syainTeate.setEnabled(false);
		syainTeate.setColumns(10);
		syainTeate.setBounds(98, 200, 96, 19);
		contentPane.add(syainTeate);
		
		syainKanri = new JTextField();
		syainKanri.setEnabled(false);
		syainKanri.setColumns(10);
		syainKanri.setBounds(98, 223, 42, 19);
		contentPane.add(syainKanri);
		
		syainKanriName = new JTextField();
		syainKanriName.setEditable(false);
		syainKanriName.setEnabled(false);
		syainKanriName.setColumns(10);
		syainKanriName.setBounds(153, 223, 147, 19);
		contentPane.add(syainKanriName);
		
		syainBirth = new JTextField();
		syainBirth.setEnabled(false);
		syainBirth.setColumns(10);
		syainBirth.setBounds(98, 246, 82, 19);
		contentPane.add(syainBirth);
		
		checkButton = new JButton("\u78BA\u8A8D");
		checkButton.setBounds(333, 28, 106, 21);
		contentPane.add(checkButton);
		
		updateButton = new JButton("\u66F4\u65B0");
		updateButton.setEnabled(false);
		updateButton.setBounds(333, 209, 106, 21);
		contentPane.add(updateButton);
		
		cancelButton = new JButton("\u30AD\u30E3\u30F3\u30BB\u30EB");
		cancelButton.setEnabled(false);
		cancelButton.setBounds(333, 247, 106, 21);
		contentPane.add(cancelButton);
		
		JLabel label = new JLabel("\u30B3\u30FC\u30C9");
		label.setBounds(31, 32, 67, 13);
		contentPane.add(label);
		
		JLabel label_1 = new JLabel("\u6C0F\u540D");
		label_1.setBounds(31, 88, 67, 13);
		contentPane.add(label_1);
		
		JLabel label_2 = new JLabel("\u30D5\u30EA\u30AC\u30CA");
		label_2.setBounds(31, 111, 67, 13);
		contentPane.add(label_2);
		
		JLabel label_3 = new JLabel("\u751F\u5E74\u6708\u65E5");
		label_3.setBounds(31, 249, 67, 13);
		contentPane.add(label_3);
		
		JLabel label_4 = new JLabel("\u6240\u5C5E");
		label_4.setBounds(31, 134, 67, 13);
		contentPane.add(label_4);
		
		JLabel label_5 = new JLabel("\u6027\u5225");
		label_5.setBounds(31, 157, 67, 13);
		contentPane.add(label_5);
		
		JLabel label_6 = new JLabel("\u7D66\u4E0E");
		label_6.setBounds(31, 180, 67, 13);
		contentPane.add(label_6);
		
		Component label_7 = new JLabel("\u624B\u5F53");
		label_7.setBounds(31, 203, 67, 13);
		contentPane.add(label_7);
		
		JLabel label_8 = new JLabel("\u7BA1\u7406\u8005");
		label_8.setBounds(31, 226, 67, 13);
		contentPane.add(label_8);	
	}

}

その他の設定

■ windowOpened イベントを追加し、表示ウインドウを中央に表示する
※ public void setLocationRelativeTo(Component c)
■ JFrame のタイトル文字列を設定する。
※ public void setTitle(String title)
■ ウインドウ枠のサイズ変更をできないようにする
※ public void setResizable(boolean resizable)
■ JLabel をローカル変数として定義し、それ以外は フィールドとして定義する

▼ このアイコンで、ローカルとフィールドの切り替えができます

※ 但し、ローカルから参照されているフィールドは、ローカルには戻せません


変数名の変更と、変数の定義位置の変更



プロパティウインドウの Variable プロパティを変更すると、ソース上で使用されている変数名が自動的に全て変更されます。デフォルトでは、WindowBuilder が作成するので、後からシステムの命名規則を元に変更するといいと思います。整理の為に、変数の順序もコンポーネントウインドウでドラッグする事によって変更可能です。

クラス名の変更

パッケージエクスプローラで選択して F2 キーで変更用のウインドウが表示されます


※ 選択して、右クリックの『リファクタリング』の中にもあります

コントロールの複製

ある程度プロパティを変更したものをそのまま複製したい場合は、デザイン画面上で選択して、CTRL+C から CTRL + V を実行すると、プロパティの複製されたコントロールを貼り付ける事のできるカーソルに変化します。
( ESC で解除 )


関連する記事


posted by lightbox at 2014-05-15 23:15 | Java | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり