トップ 最新 追記

Masa's blog

検索キーワード:

2010年11月02日 Java儂(わし)的解釈によるメモ(Swing編) [長年日記]

_ Java儂(わし)的解釈によるメモ(Swing編)

JavaでのGUIプログラミングに挑んでみる編。

JLabel(ラベル(文字列)表示)

//
// JLabelTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJLabel(ラベルオブジェクト)をラップする自前のクラス
//
class Label {
	//
	// JLabel(ラベルオブジェクト)の定義
	//
	JLabel object;
	//
	// コンストラクタ
	//
	//   JLabel(ラベルオブジェクト)の生成(ここでデザインの等の決定)
	//
	Label(String s){
		object = new JLabel(s);
	}
	//
	// JLabel(ラベルオブジェクト)の取得
	//
	JLabel getObject(){
		return object;
	}
}
//
// メインのクラス
//
class JLabelTestMain {
	public static void main(String [] args){
		JLabelTestMain m = new JLabelTestMain();
		//
		// JLabelの独自ラッパーオブジェクトの生成
		//
		Label lb = new Label("ラベル文字列");
		//
		// JLabelオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJLabelオブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にJLabelオブジェクトを配置
		//
		p.add(lb.getObject(), BorderLayout.CENTER);

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JLabelTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • フレームがGUIアプリケーションの土台となる。
  • フレーム全体にコンテナがかぶさっていて、実際にはこの上にパネルを配置する。
  • JLabel等のGUI部品パネル上に配置する。
  • パネル上にパネルを配置するのもあり。複雑なデザインを実現するには不可欠な手法。

絵にすると以下のような感じ...

   [GUIオブジェクト][GUIオブジェクト] ... [GUIオブジェクト]  ......       ......
  [                    パ  ネ  ル                          ][パネル] ... [パネル]
 [                                 コ  ン  テ  ナ                                ]
[                              フ    レ    ー    ム                               ]

JButton(単独のボタン)

//
// JButtonTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJButton(ボタンオブジェクト)をラップする自前のクラス
//
class Button {
	//
	// JButton(ボタンオブジェクト)の定義
	//
	JButton object;
	//
	// コンストラクタ
	//
	//   JButton(ボタンオブジェクト)の生成(ここでデザインの等の決定)
	//
	Button(String s){
		object = new JButton(s);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	//
	// JButton(ボタンオブジェクト)の取得
	//
	JButton getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class Button_Listener implements ActionListener {
	//
	// Button(自前ボタンオブジェクト)の定義
	//
	Button so;
	//
	// コンストラクタ
	//
	//   イベントの発生元オブジェクトの設定
	//
	Button_Listener(Button so){
		this.so = so;
	}
	//
	// イベントハンドラ本体
	//
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("OK1");
		}
	}
}
//
// メインのクラス
//
class JButtonTestMain {
	public static void main(String [] args){
		JButtonTestMain m = new JButtonTestMain();
		//
		// JButtonの独自ラッパーオブジェクトの生成
		//
		Button b = new Button("OK1");
		//
		// JButtonの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		b.setListener(new Button_Listener(b));

		//
		// JButtonオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJButtonオブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にJButtonオブジェクトを配置
		//
		p.add(b.getObject(), BorderLayout.CENTER);

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • イベントハンドラの登録は JButtonオブジェクト.addActionListener(ActionListener イベントハンドラオブジェクト)
  • イベントハンドラオブジェクトは必ずActionListenerをインプリメントする。
  • イベントハンドラオブジェクト内にactionPerformed(ActionEvent e)の形式でイベントハンドラメソッドを定義する。

JRadioButton(ラジオボタン)

//
// JRadioButtonTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJRadioButton(ラジオボタンオブジェクト)をラップする自前のクラス
//
class RadioButton {
	//
	// JRadioButton(ラジオボタンオブジェクト)の定義
	//
	JRadioButton object;
	//
	// その他変数
	//
	String string;
	//
	// コンストラクタ
	//
	//   JRadioButton(ラジオボタンオブジェクト)の生成(ここでデザインの等の決定)
	//
	RadioButton(String s){
		object = new JRadioButton(s);
		string = s;
	}
	RadioButton(String s, Boolean b){
		if (b){
			object = new JRadioButton("[" + s + "]", b);
		}else{
			object = new JRadioButton(s, b);
		}
		string = s;
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ChangeListener eh){
		object.addChangeListener(eh);
	}
	//
	// JRadioButton(ラジオボタンオブジェクト)の取得
	//
	JRadioButton getObject(){
		return object;
	}
	//
	// stringの取得
	//
	String getString(){
		return string;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class RadioButton_Listener implements ChangeListener {
	//
	// RadioButton(自前ラジオボタンオブジェクト)の定義
	//
	RadioButton so;
	//
	// コンストラクタ
	//
	//   イベントの発生元オブジェクトの設定
	//
	RadioButton_Listener(RadioButton so){
		this.so = so;
	}
	//
	// イベントハンドラ本体
	//
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == so.getObject()){
			if (so.getObject().isSelected()){
				so.getObject().setText("[" + so.getString() + "]");
			}else{
				so.getObject().setText(so.getString());
			}
		}
	}
}
//
// メインのクラス
//
class JRadioButtonTestMain {
	public static void main(String [] args){
		JRadioButtonTestMain m = new JRadioButtonTestMain();
		//
		// JRadioButtonの独自ラッパーオブジェクトの生成
		//
		RadioButton rb1 = new RadioButton("apple", true);
		RadioButton rb2 = new RadioButton("orange");
		RadioButton rb3 = new RadioButton("lemon");
		//
		// JRadioButtonの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		rb1.setListener(new RadioButton_Listener(rb1));
		rb2.setListener(new RadioButton_Listener(rb2));
		rb3.setListener(new RadioButton_Listener(rb3));
		//
		// JRadioButtonオブジェクトをグループ化
		//
		ButtonGroup bg = new ButtonGroup();
		bg.add(rb1.getObject());
		bg.add(rb2.getObject());
		bg.add(rb3.getObject());

		//
		// JRadioButtonオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJRadioButtonオブジェクトを配置する方法の設定
		//
		p.setLayout(new FlowLayout());
		//
		// パネル上にJRadioButtonオブジェクトを配置
		//
		p.add(rb1.getObject());
		p.add(rb2.getObject());
		p.add(rb3.getObject());

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JRadioButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • イベントハンドラの登録は JRadioButtonオブジェクト.addChangeListener(ChangeListener イベントハンドラオブジェクト)
  • イベントハンドラオブジェクトは必ずChangeListenerをインプリメントする。
  • イベントハンドラオブジェクト内にstateChanged(ChangeEvent e)の形式でイベントハンドラメソッドを定義する。

JCheckBox(チェックボックス)

//
// JCheckBoxTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJCheckBox(チェックボックスオブジェクト)をラップする自前のクラス
//
class CheckBox {
	//
	// JCheckBox(チェックボックスオブジェクト)の定義
	//
	JCheckBox object;
	//
	// コンストラクタ
	//
	//   JCheckBox(チェックボックスオブジェクト)の生成(ここでデザインの等の決定)
	//
	CheckBox(String s){
		object = new JCheckBox(s);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ChangeListener eh){
		object.addChangeListener(eh);
	}
	//
	// JCheckBox(チェックボックスオブジェクト)の取得
	//
	JCheckBox getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class CheckBox_Listener implements ChangeListener {
	//
	// CheckBox(自前チェックボックスオブジェクト)の定義
	//
	CheckBox so;
	//
	// コンストラクタ
	//
	//   イベントの発生元オブジェクトの設定
	//
	CheckBox_Listener(CheckBox so){
		this.so = so;
	}
	//
	// イベントハンドラ本体
	//
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == so.getObject()){
			if (so.getObject().isSelected()){
				so.getObject().setText("ON");
			}else{
				so.getObject().setText("OFF");
			}
		}
	}
}
//
// メインのクラス
//
class JCheckBoxTestMain {
	public static void main(String [] args){
		JCheckBoxTestMain m = new JCheckBoxTestMain();
		//
		// JCheckBoxの独自ラッパーオブジェクトの生成
		//
		CheckBox cb = new CheckBox("OFF");
		//
		// JCheckBoxの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		cb.setListener(new CheckBox_Listener(cb));

		//
		// JCheckBoxオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJCheckBoxオブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にJCheckBoxオブジェクトを配置
		//
		p.add(cb.getObject(), BorderLayout.CENTER);

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JCheckBoxTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • イベントハンドラの登録は JCheckBoxオブジェクト.addChangeListener(ChangeListener イベントハンドラオブジェクト)
  • イベントハンドラオブジェクトは必ずChangeListenerをインプリメントする。
  • イベントハンドラオブジェクト内にstateChanged(ChangeEvent e)の形式でイベントハンドラメソッドを定義する。

JList(選択一覧リスト)

//
// JListTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJList(選択一覧リスト)をラップする自前のクラス
//
class List {
	//
	// JList(選択一覧リストオブジェクト)の定義
	//
	JList object;
	//
	// コンストラクタ
	//
	//   JList(選択一覧リストオブジェクト)の生成(ここでデザインの等の決定)
	//
	List(String [] s){
		object = new JList(s);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ListSelectionListener eh){
		object.addListSelectionListener(eh);
	}
	//
	// JList(選択一覧リストオブジェクト)の取得
	//
	JList getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class List_Listener implements ListSelectionListener {
	//
	// List(自前選択一覧リストオブジェクト)の定義
	//
	List so;
	//
	// コンストラクタ
	//
	//   イベントの発生元オブジェクトの設定
	//
	List_Listener(List so){
		this.so = so;
	}
	//
	// イベントハンドラ本体
	//
	public void valueChanged(ListSelectionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(so.getObject().getSelectedValue());
		}
	}
}
//
// メインのクラス
//
class JListTestMain {
	public static void main(String [] args){
		JListTestMain m = new JListTestMain();
		//
		// JListの独自ラッパーオブジェクトの生成
		//
		String [] lists = {"apple", "orange", "lemon"};
		List ls = new List(lists);
		//
		// JListの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		ls.setListener(new List_Listener(ls));

		//
		// JListオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJListオブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にJListオブジェクトを配置
		//
		p.add(ls.getObject(), BorderLayout.CENTER);

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JListTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • イベントハンドラの登録は JListオブジェクト.addListSelectionListener(ListSelectionListener イベントハンドラオブジェクト)
  • イベントハンドラオブジェクトは必ずListSelectionListenerをインプリメントする。
  • イベントハンドラオブジェクト内にvalueChanged(ListSelectionEvent e)の形式でイベントハンドラメソッドを定義する。

JSlider(スライダー)

//
// JSliderTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJSlider(スライダー)をラップする自前のクラス
//
class Slider {
	//
	// JSlider(スライダーオブジェクト)の定義
	//
	JSlider object;
	//
	// コンストラクタ
	//
	//   JSlider(スライダーオブジェクト)の生成(ここでデザインの等の決定)
	//
	Slider(int min, int max, int val){
		object = new JSlider(min, max, val);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ChangeListener eh){
		object.addChangeListener(eh);
	}
	//
	// JSlider(スライダーオブジェクト)の取得
	//
	JSlider getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class Slider_Listener implements ChangeListener {
	//
	// Slider(自前スライダーオブジェクト)の定義
	//
	Slider so;
	//
	// コンストラクタ
	//
	//   イベントの発生元オブジェクトの設定
	//
	Slider_Listener(Slider so){
		this.so = so;
	}
	//
	// イベントハンドラ本体
	//
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(so.getObject().getValue());
		}
	}
}
//
// メインのクラス
//
class JSliderTestMain {
	public static void main(String [] args){
		JSliderTestMain m = new JSliderTestMain();
		//
		// JSliderの独自ラッパーオブジェクトの生成
		//
		Slider sl = new Slider(0, 100 , 50);
		//
		// JSliderの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		sl.setListener(new Slider_Listener(sl));

		//
		// JSliderオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJSliderオブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にJSliderオブジェクトを配置
		//
		p.add(sl.getObject(), BorderLayout.CENTER);

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JSliderTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • イベントハンドラの登録は JSliderオブジェクト.addChangeListener(ChangeListener イベントハンドラオブジェクト)
  • イベントハンドラオブジェクトは必ずChangeListenerをインプリメントする。
  • イベントハンドラオブジェクト内にstateChanged(ChangeEvent e)の形式でイベントハンドラメソッドを定義する。

JTextField(一行テキスト入力)

//
// JTextFieldTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJTextField(一行テキスト入力)をラップする自前のクラス
//
class TextField {
	//
	// JTextField(一行テキスト入力オブジェクト)の定義
	//
	JTextField object;
	//
	// コンストラクタ
	//
	//   JTextField(一行テキスト入力オブジェクト)の生成(ここでデザインの等の決定)
	//
	TextField(String s, int col){
		object = new JTextField(s, col);
	}
	//
	// JTextField(一行テキスト入力オブジェクト)の取得
	//
	JTextField getObject(){
		return object;
	}
}
//
// GUIの基本部品であるJButton(単独ボタン)をラップする自前のクラス
//
class Button {
	//
	// JButton(単独ボタンオブジェクト)の定義
	//
	JButton object;
	//
	// コンストラクタ
	//
	//   JButton(単独ボタンオブジェクト)の生成(デザインの決定等)
	//
	Button(String s){
		object = new JButton(s);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	//
	// JButton(単独ボタンオブジェクト)の取得
	//
	JButton getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class Button_Listener implements ActionListener {
	//
	// Button(自前単独ボタンオブジェクト)の定義
	// TextField(自前一行テキスト入力オブジェクト)の定義
	//
	Button so;
	TextField tf;
	//
	// コンストラクタ
	//
	//   イベントの発生元(等)オブジェクトの設定
	//
	Button_Listener(Button so, TextField tf){
		this.so = so;
		this.tf = tf;
	}
	//
	// イベントハンドラ本体
	//
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(tf.getObject().getText());
		}
	}
}
//
// メインのクラス
//
class JTextFieldTestMain {
	public static void main(String [] args){
		JTextFieldTestMain m = new JTextFieldTestMain();
		//
		// JTextFieldの独自ラッパーオブジェクトの生成
		// JButtonの独自ラッパーオブジェクトの生成
		//
		TextField tf = new TextField("初期文字列", 20);
		Button b = new Button("Get!");
		//
		// JButtonの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		b.setListener(new Button_Listener(b, tf));

		//
		// JTextFieldオブジェクト、JButtonオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にJTextFieldオブジェクト、JButtonオブジェクトを配置する方法の設定
		//
		p.setLayout(new FlowLayout());
		//
		// パネル上にJTextFieldオブジェクト、JButtonオブジェクトを配置
		//
		p.add(tf.getObject());
		p.add(b.getObject());

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JTextFieldTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}

JTextArea(複数行テキスト入力)

//
// JTextAreaTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// GUIの基本部品であるJTextArea(複数行テキスト入力)をラップする自前のクラス
//
class TextArea {
	//
	// JTextArea(複数行テキスト入力オブジェクト)の定義
	//
	JTextArea object;
	//
	// コンストラクタ
	//
	//   JTextArea(複数行テキスト入力オブジェクト)の生成(デザインの決定等)
	//
	TextArea(String s, int row, int col){
		object = new JTextArea(s, row, col);
	}
	//
	// JTextArea(複数行テキスト入力オブジェクト)の取得
	//
	JTextArea getObject(){
		return object;
	}
}
//
// GUIの基本部品であるJButton(単独ボタン)をラップする自前のクラス
//
class Button {
	//
	// JButton(単独ボタンオブジェクト)の定義
	//
	JButton object;
	//
	// コンストラクタ
	//
	//   JButton(単独ボタンオブジェクト)の生成(デザインの決定等)
	//
	Button(String s){
		object = new JButton(s);
	}
	//
	// イベントハンドラの登録
	//
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	//
	// JButton(単独ボタンオブジェクト)の取得
	//
	JButton getObject(){
		return object;
	}
}
//
// イベントハンドラを定義する独自クラス(リスナーオブジェクト)
//
class Button_Listener implements ActionListener {
	//
	// Button(自前単独ボタンオブジェクト)の定義
	// TextArea(自前複数行テキスト入力オブジェクト)の定義
	//
	Button so;
	TextArea ta;
	//
	// コンストラクタ
	//
	//   イベントの発生元(等)オブジェクトの設定
	//
	Button_Listener(Button so, TextArea ta){
		this.so = so;
		this.ta = ta;
	}
	//
	// イベントハンドラ本体
	//
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(ta.getObject().getText());
		}
	}
}
//
// メインのクラス
//
class JTextAreaTestMain {
	public static void main(String [] args){
		JTextAreaTestMain m = new JTextAreaTestMain();
		//
		// JTextFieldの独自ラッパーオブジェクトの生成
		// GUIの基本部品であるJScrollPaneオブジェクトの生成
		// JButtonの独自ラッパーオブジェクトの生成
		//
		TextArea ta = new TextArea("初期文字列", 5, 20);
		JScrollPane sp = new JScrollPane(ta.getObject());
		Button b = new Button("Get!");
		//
		// JButtonの独自ラッパーオブジェクトに対するイベントハンドラの登録
		//
		b.setListener(new Button_Listener(b, ta));

		//
		// JScrollPaneオブジェクト、JButtonオブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// JScrollPaneオブジェクト、JButtonオブジェクトを配置する方法の設定
		//
		p.setLayout(new FlowLayout());
		//
		// JScrollPaneオブジェクト、JButtonオブジェクトを配置
		//
		p.add(sp);
		p.add(b.getObject());

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JTextAreaTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}

FlowLayout(フローレイアウト方式 - 左から右へ流し込み)

//
// JFlowLayoutTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
class Button {
	JButton object;
	String string;
	Button(String s){
		object = new JButton(s);
		string = s;
	}
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	JButton getObject(){
		return object;
	}
}
//
class Button_Listener implements ActionListener {
	Button so;
	Button_Listener(Button so){
		this.so = so;
	}
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(so.string);
		}
	}
}
//
class JFlowLayoutTestMain {
	public static void main(String [] args){
		JFlowLayoutTestMain m = new JFlowLayoutTestMain();

		Button b1 = new Button("OK1");
		Button b2 = new Button("OK2");
		Button b3 = new Button("OK3");
		b1.setListener(new Button_Listener(b1));
		b2.setListener(new Button_Listener(b2));
		b3.setListener(new Button_Listener(b3));

		//
		// GUI部品オブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にGUI部品オブジェクトを配置する方法の設定
		//
		p.setLayout(new FlowLayout());
		//
		// パネル上にGUI部品オブジェクトを配置
		//
		p.add(b1.getObject());
		p.add(b2.getObject());
		p.add(b3.getObject());

		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setLayout(new BorderLayout());
		f.setTitle("JButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		Container c = f.getContentPane();
		c.add(p, BorderLayout.CENTER);
		f.pack();
		f.setVisible(true);
	}
}

BorderLayout(ボーダーレイアウト方式 - 東西南北中央)

//
// JBorderLayoutTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
class Button {
	JButton object;
	String string;
	Button(String s){
		object = new JButton(s);
		string = s;
	}
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	JButton getObject(){
		return object;
	}
}
//
class Button_Listener implements ActionListener {
	Button so;
	Button_Listener(Button so){
		this.so = so;
	}
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(so.string);
		}
	}
}
//
class JBorderLayoutTestMain {
	public static void main(String [] args){
		JBorderLayoutTestMain m = new JBorderLayoutTestMain();

		Button b1 = new Button("North");
		Button b2 = new Button("East");
		Button b3 = new Button("South");
		Button b4 = new Button("West");
		Button b5 = new Button("Center");
		b1.setListener(new Button_Listener(b1));
		b2.setListener(new Button_Listener(b2));
		b3.setListener(new Button_Listener(b3));
		b4.setListener(new Button_Listener(b4));
		b5.setListener(new Button_Listener(b5));

		//
		// GUI部品オブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にGUI部品オブジェクトを配置する方法の設定
		//
		p.setLayout(new BorderLayout());
		//
		// パネル上にGUI部品オブジェクトを配置
		//
		p.add(b1.getObject(), BorderLayout.NORTH);
		p.add(b2.getObject(), BorderLayout.EAST);
		p.add(b3.getObject(), BorderLayout.SOUTH);
		p.add(b4.getObject(), BorderLayout.WEST);
		p.add(b5.getObject(), BorderLayout.CENTER);

		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setLayout(new BorderLayout());
		f.setTitle("JButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		Container c = f.getContentPane();
		c.add(p, BorderLayout.CENTER);
		f.pack();
		f.setVisible(true);
	}
}

GridLayout(グリッドレイアウト方式 - 格子状に配置)

//
// JGridLayoutTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
class Button {
	JButton object;
	String string;
	Button(String s){
		object = new JButton(s);
		string = s;
	}
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}
	JButton getObject(){
		return object;
	}
}
//
class Button_Listener implements ActionListener {
	Button so;
	Button_Listener(Button so){
		this.so = so;
	}
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println(so.string);
		}
	}
}
//
class JGridLayoutTestMain {
	public static void main(String [] args){
		JGridLayoutTestMain m = new JGridLayoutTestMain();

		Button b1 = new Button("OK1");
		Button b2 = new Button("OK2");
		Button b3 = new Button("OK3");
		Button b4 = new Button("OK4");
		Button b5 = new Button("OK5");
		Button b6 = new Button("OK6");
		b1.setListener(new Button_Listener(b1));
		b2.setListener(new Button_Listener(b2));
		b3.setListener(new Button_Listener(b3));
		b4.setListener(new Button_Listener(b4));
		b5.setListener(new Button_Listener(b5));
		b6.setListener(new Button_Listener(b6));

		//
		// GUI部品オブジェクトを配置するパネルの生成
		//
		JPanel p = new JPanel();
		//
		// パネル上にGUI部品オブジェクトを配置する方法の設定
		//
		p.setLayout(new GridLayout(3,2));
		//
		// パネル上にGUI部品オブジェクトを配置
		//
		p.add(b1.getObject());
		p.add(b2.getObject());
		p.add(b3.getObject());
		p.add(b4.getObject());
		p.add(b5.getObject());
		p.add(b6.getObject());

		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setLayout(new BorderLayout());
		f.setTitle("JButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		Container c = f.getContentPane();
		c.add(p, BorderLayout.CENTER);
		f.pack();
		f.setVisible(true);
	}
}

グラフィックス描画

//
// JGraphTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
// 描画の対象となるオブジェクト
//
class MyPanel extends JPanel {
	//
	// 描画に使用されるパラメータ
	//
	int x = 50;
	int y = 100;
	Graphics g;
	//
	// コンストラクタ
	//
	public MyPanel() {
		setBackground(Color.white);
	}
	//
	// イベントハンドラからのパラメータ設定
	//
	public void setXY(int x, int y) {
		this.x = x;
		this.y = y;
	}
	//
	// イベントハンドラからの再描画
	//
	public void repaint() {
		super.repaint();
	}
	//
	//
	//
	public Graphics getG() {
		return g;
	}
	//
	// 描画処理
	//
	public void paintComponent(Graphics g) {
		this.g = g;

		super.paintComponent(g);

		// ウィンドウ内に描画したい図形をここに書く
		g.setColor(Color.red);
		g.fillRect(x,y,100,100);
		g.drawRect(x+150,y,100,100);

		g.setColor(Color.black);
		g.drawLine(x+300,y,x+400,y+100);

		g.setColor(Color.gray);
		g.fillOval(x,y+140,100,100);
		g.drawOval(x+150,y+140,100,100);

		g.setColor(Color.black);
		g.drawString("Welcome to java graphics.", x-40, y-80);
	}
}

//
// イベントハンドラのオブジェクト
//
class MyPanel_Listener implements MouseMotionListener{
	//
	// イベントを受け取るオブジェクト
	//
	MyPanel p;
	//
	// コンストラクタ
	//
	public MyPanel_Listener (MyPanel p){
		this.p = p;
	}
	//
	// イベントハンドラ本体 for MouseMotionListener
	//
	public void mouseDragged(MouseEvent e) {
		if (e.getSource() == p){
			p.getG().setColor(Color.blue);
			p.getG().fillRect(e.getX(),e.getY(),400,400);
			p.setXY(e.getX(),e.getY());
			p.repaint();
		}
	}
	public void mouseMoved(MouseEvent e) {
	}
}

class JGraphTestMain {
	public static void main(String [] args){
		JGraphTestMain m = new JGraphTestMain();
		//
		// GUI部品ソースオブジェクトの生成
		//
		MyPanel p = new MyPanel();
		//
		// マウス用移動イベントハンドラの登録
		//
		p.addMouseMotionListener(new MyPanel_Listener(p));

		//
		// パネルを配置するためのフレーム(ウインドウ)を生成
		//
		JFrame f = new JFrame();
		//
		// パネルを閉じた時の動作を設定(終了)
		//
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//
		// フレーム上にパネルを配置する方法の設定
		//
		f.setLayout(new BorderLayout());
		//
		// フレームに関するもろもろの設定
		//
		f.setTitle("JButtonTest");
		f.setSize(640,480);
		f.setLocation(300,300);
		//
		// フレームからコンテナ(表示領域)の取得
		//
		Container c = f.getContentPane();
		//
		// パネルをコンテナ上に配置
		//
		c.add(p, BorderLayout.CENTER);
		//
		// 余分な空白を詰め合わせる
		//
		//f.pack();
		//
		// 可視化する
		//
		f.setVisible(true);
	}
}
  • 描画の対象となるオブジェクトはJPanelクラスを継承する。
  • 描画の対象となるオブジェクト内にpaintComponent(Graphics g)メソッドを定義し、その中で描画処理を行う。
  • 描画処理を行うpaintComponent(Graphics g)メソッドは直接呼び出さずに、親クラスであるJPanelrepaint()メソッドを呼び出す。

マウスイベント処理

//
// JMouseTestMain.java
//
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//
class Button {
	JButton object;
	Button(String s){
		object = new JButton(s);
	}
	void setListener(ActionListener eh){
		object.addActionListener(eh);
	}

	//
	// マウス用操作イベントハンドラの登録
	//
	void setMouseListener(MouseListener eh){
		object.addMouseListener(eh);
	}
	//
	// マウス用移動イベントハンドラの登録
	//
	void setMouseMotionListener(MouseMotionListener eh){
		object.addMouseMotionListener(eh);
	}

	JButton getObject(){
		return object;
	}
}
//
class Button_Listener implements ActionListener {
	Button so;
	Button_Listener(Button so){
		this.so = so;
	}
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("OK1");
		}
	}
}

//
// マウス用操作リスナーオブジェクト
//
class Button_Mouse_Listener implements MouseListener {
	//
	// GUI部品ソースオブジェクト
	//
	Button so;
	//
	// コンストラクタ:処理するイベントの発生元GUIソースオブジェクトの設定
	//
	Button_Mouse_Listener(Button so){
		this.so = so;
	}
	//
	// イベントハンドラ本体 for MouseListener
	//
	public void mousePressed(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mousePressed at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
	public void mouseReleased(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseReleased at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
	public void mouseClicked(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseClicked at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
	public void mouseEntered(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseEntered at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
	public void mouseExited(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseExited at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
}

//
// GUI部品マウス用移動リスナーオブジェクト
//
class Button_Mouse_Motion_Listener implements MouseMotionListener {
	//
	// GUI部品ソースオブジェクト
	//
	Button so;
	//
	// コンストラクタ:処理するイベントの発生元GUIソースオブジェクトの設定
	//
	Button_Mouse_Motion_Listener(Button so){
		this.so = so;
	}
	//
	// イベントハンドラ本体 for MouseMotionListener
	//
	public void mouseDragged(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseDragged at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
	public void mouseMoved(MouseEvent e) {
		if (e.getSource() == so.getObject()){
			System.out.println("mouseMoved at " + Integer.toString(e.getX()) + "," + Integer.toString(e.getY()));
		}
	}
}

class JMouseTestMain {
	public static void main(String [] args){
		JMouseTestMain m = new JMouseTestMain();

		Button b = new Button("OK1");
		b.setListener(new Button_Listener(b));
		//
		// マウス用操作イベントハンドラの登録
		//
		b.setMouseListener(new Button_Mouse_Listener(b));
		//
		// マウス用移動イベントハンドラの登録
		//
		b.setMouseMotionListener(new Button_Mouse_Motion_Listener(b));

		JPanel p = new JPanel();
		p.setLayout(new BorderLayout());
		p.add(b.getObject(), BorderLayout.CENTER);

		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setLayout(new BorderLayout());
		f.setTitle("JButtonTest");
		// f.setSize(200,200);
		f.setLocation(300,300);
		Container c = f.getContentPane();
		c.add(p, BorderLayout.CENTER);
		f.pack();
		f.setVisible(true);
	}
}
  • マウスボタンのプレスリリースクリック、マウスカーソルがオブジェクトに入った出ていったの5種類のイベントを処理するハンドラをaddMouseListener(MouseListener eh)メソッドで登録する。
  • マウスカーソルのドラグ移動の2種類のイベントを処理するハンドラをaddMouseMotionListener(MouseMotionListener eh)メソッドで登録する。
  • addMouseListener()で登録するハンドラオブジェクトはMouseListenerインターフェースをインプリメント(mousePressed()mouseReleased()mouseClicked()mouseEntered()mouseExited()のメソッドを定義)する。
  • addMouseMotionListener()で登録するハンドラオブジェクトはMouseMotionListenerインターフェースをインプリメント(mouseDragged()mouseMoved()のメソッドを定義)する。

2010年11月04日 proxy環境下のWindows7におけるMicrosoft Security Essentialsの定義ファイル更新 [長年日記]

_ proxy環境下のWindows7におけるMicrosoft Security Essentialsの定義ファイル更新

IEのプロキシ設定とは別に、Microsoft Updateのプロキシ設定を行わないと、定義ファイルの更新ができない。

コマンドプロンプトにて、

netsh set proxy 192.168.0.100:8080
  • 192.168.0.100 : プロキシサーバのIPアドレス
  • 8080 : プロキシデーモンのポート番号

2010年11月11日 Java儂(わし)的解釈によるメモ(マッチング処理編) [長年日記]

_ Java儂(わし)的解釈によるメモ(マッチング処理編)

いわゆるマッチング処理によるマスターファイル更新的なサンプルプログラム。

初めてJavaで業務処理(バッチ処理)的なものを実装。COBOLERパワー全開の芳ばしい一品にしあがっておる。

MasterRecord クラス

COBOLプログラムで言うところのレコード定義COPY原文に相当するのか。

ただし、あくまでもこれはクラスなので、レコードレイアウトの項目定義だけでなく、取り扱いに関する各種メソッドも実装する。

//
// レコードクラス
//
//   業務ファイルのレイアウト毎に色々と定義されるであろうクラス
//
class MasterRecord {
	//
	// 項目定義
	//
	int id;
	String name;
	String job;
	//
	// コンストラクタ(生成時の初期化処理)
	//
	MasterRecord() {
		initialize();
	}
	//
	// 初期化処理
	//
	void initialize() {
		id = 0;
		name = null;
		job = null;
	}
	//
	// TAB区切りレコードから項目に取り込み
	//
	String set(String record) {
		if (record != null){
			String[] array = record.split("\t");
			id = Integer.valueOf(array[0]);
			name = array[1];
			job = array[2];
		}
		return record;
	}
	//
	// 項目からTAB区切りレコードを取得
	//
	String get() {
		return String.format("%d\t%s\t%s", id, name, job);
	}
	//
	// 各項目に値を設定
	//
	void setId(int id) {
		this.id = id;
	}
	void setName(String name) {
		this.name = name;
	}
	void setJob(String job) {
		this.job = job;
	}
	//
	// 各項目の値を取得
	//
	int getId() {
		return id;
	}
	String getName() {
		return name;
	}
	String getJob() {
		return job;
	}
}

TextFile クラス

レコードが改行文字で終端するファイル(いわゆるテキストファイル)を取り扱うためのクラス。

import java.io.*;
//
// テキストファイルクラス
//
class TextFile {
	String file_name;
	String mode;
	String encoding;
	BufferedReader br;
	BufferedWriter bw;
	long read_count;
	long write_count;
	//
	// コンストラクタ
	//
	TextFile() {
		file_name = null;
		mode = null;
		encoding = "EUC-JP";
		br = null;
		bw = null;
		read_count = 0;
		write_count = 0;
	}
	//
	// open(1)
	//
	// 	String file_name	ファイル名称
	// 	String mode		オープンモード("r"|"w"|"a")
	// 	ファイルのエンコーディングはコンストラクタでの設定になります
	//
	//	戻り値	正常 : true
	//		異常 : false
	//
	boolean open(String file_name, String mode) throws Exception {
		this.file_name = file_name;
		this.mode = mode;

		read_count = 0;
		write_count = 0;

		if (mode.equals("r")){
			br = new BufferedReader(
				new InputStreamReader(
					new FileInputStream(file_name),
					encoding
				)
			);
		}else if(mode.equals("w")|mode.equals("a")){
			bw = new BufferedWriter(
				new OutputStreamWriter(
					new FileOutputStream(file_name, (mode.equals("a") ? true : false)),
					encoding
				)
			);
		}else{
			return false;
		}
		return true;
	}
	//
	// open(2)
	//
	// 	String file_name	ファイル名称
	// 	String mode		オープンモード("r"|"w"|"a")
	//	String encoding		エンコーディング
	//
	//	戻り値	正常 : true
	//		異常 : false
	//
	boolean open(String file_name, String mode, String encoding) throws Exception {
		this.file_name = file_name;
		this.mode = mode;
		this.encoding = encoding;
		return open(file_name, mode);
	}
	//
	// 一行読み込み
	//
	//	戻り値	正常 : レコード
	//		終了 : null
	//
	String read() throws Exception {
		String record;
		if ((record = br.readLine()) != null){
			read_count++;
		}
		return record;
	}
	//
	// 一行書き込み
	//
	//	record	書き込むレコード(改行含めない)
	//
	void write(String record) throws Exception {
		bw.write(record);
		bw.newLine();
		write_count++;
	}
	//
	// close
	//
	void close() throws Exception {
		read_count = 0;
		write_count = 0;

		if (br != null){
			br.close();
			br = null;
		}
		if (bw != null){
			bw.close();
			bw = null;
		}
	}
	//
	// flush
	//
	void flush() throws Exception {
		if (bw != null){
			bw.flush();
		}
	}
	//
	// 読み込み件数取得
	//
	long getReadCount(){
		return read_count;
	}
	//
	// 書き込み件数取得
	//
	long getWriteCount(){
		return write_count;
	}
}

CsvTest.java メインクラス

情報処理試験にでも出てきそうなマッチング処理によるマスタファイル更新処理。

いちおうCURDを実装している。

//
// マスターファイルをトランザクションファイルで更新した結果を出力する処理
//
// 	マスター(入力) TAB区切りファイル
// 		int	id	(昇順)
// 		String	name
// 		String	job
//
// 	トランザクション(入力) TAB区切りファイル
// 		int	kbn	0:削除, 1:追加, 2:修正
// 		int	id	(昇順)
// 		String	name
// 		String	job
//
// 	更新後マスター(出力) TAB区切りファイル
// 		int	id	(昇順)
// 		String	name
// 		String	job
//
// 	(1) マスタファイルとトランザクションファイルをidで1対1のマッチングを行う
// 	(2) マッチングした場合はトランザクションファイルのデータを出力する
// 	(3) マスターファイルのみ有りのデータはそのまま出力する
// 	(4) トランザクションファイルのみ有りのデータは廃棄する
//
// 定数クラス
//
class Constant {
	String PGMNAME = "CsvTest";
	String DEF_MASTFILENAME = "CsvTest_mast.txt";
	String DEF_TRANFILENAME = "CsvTest_tran.txt";
	String DEF_OUTFILENAME = "CsvTest_out.txt";
	String DEF_ENCODING = "EUC-JP";
	int KBN_DELETE = 0;
	int KBN_INSERT = 1;
	int KBN_UPDATE = 2;
}
//
// トランザクションレコードクラス
//
class MasterRecordTran extends MasterRecord {
	int kbn;

	MasterRecordTran() {
		kbn = 0;
	}

	void initialize() {
		super.initialize();
		kbn = 0;
	}

	String set(String record) {
		if (record != null){
			String[] array = record.split("\t", 2);
			kbn = Integer.valueOf(array[0]);
			super.set(array[1]);
		}
		return record;
	}

	String get() {
		return String.format("%d\t%s", kbn, super.get());
	}

	int getKbn() {
		return kbn;
	}

	void setKbn(int kbn) {
		this.kbn = kbn;
	}
}
//
// スイッチクラス
//
class Switch {
	boolean state;
	Switch(){
		state = false;
	}
	Switch(boolean state){
		this.state = state;
	}
	void on(){
		state = true;
	}
	void off(){
		state = false;
	}
	boolean is_on(){
		return (state == true);
	}
	boolean is_off(){
		return (state == false);
	}
}
//
// メインクラス
//
class CsvTest {
	//
	// 定数オブジェクト
	//
	Constant cs = new Constant();
	//
	// マスターファイル&レコードオブジェクト
	//
	TextFile mastFile = new TextFile();
	MasterRecord mastRec = new MasterRecord();
	//
	// トランザクションファイル&レコードオブジェクト
	//
	TextFile tranFile = new TextFile();
	MasterRecordTran tranRec = new MasterRecordTran();
	//
	// 出力ファイル&レコードオブジェクト
	//
	TextFile outFile = new TextFile();
	MasterRecord outRec = new MasterRecord();
	//
	// 処理終了スイッチ
	//
	Switch endSw = new Switch();
	//
	// メイン処理
	//
	public static void main(String[] args) throws Exception {
		CsvTest m = new CsvTest();
		try {
			m.openProc();		// オープン処理
			m.initProc();		// 初期処理
			while (m.endSw.is_off()){
				m.mainProc();	// 主処理
			}
			m.endProc();		// 終了処理
		}catch (Exception e){
			System.out.println(m.cs.PGMNAME + " : 例外エラーが発生したので強制終了します : " + e);
		}finally{
			m.closeProc();		// クローズ処理
		}
	}
	//
	// オープン処理
	//
	void openProc() throws Exception {
		//
		// マスターオープン
		//
		mastFile.open(cs.DEF_MASTFILENAME, "r", cs.DEF_ENCODING);
		//
		// トランザクションオープン
		//
		tranFile.open(cs.DEF_TRANFILENAME, "r", cs.DEF_ENCODING);
		//
		// 更新後マスターオープン
		//
		outFile.open(cs.DEF_OUTFILENAME, "w", cs.DEF_ENCODING);
	}
	//
	// 初期処理
	//
	void initProc() throws Exception {
		System.out.println(cs.PGMNAME + " : マスター更新処理 開始");
		//
		// マスター読み込み
		//
		readMast();
		//
		// トランザクション読み込み
		//
		readTran();
		//
		// 終了判断
		//
		setEndSw();
	}
	//
	// 主処理
	//
	void mainProc() throws Exception {
		if (mastRec.getId() == tranRec.getId()){
			//
			// マッチした場合
			//
			if (tranRec.getKbn() == cs.KBN_DELETE){
				// do nothing
			}else if (tranRec.getKbn() == cs.KBN_INSERT){
				System.out.println("既にデータが存在するので追加できません : " + tranRec.get());
				outRec.set(mastRec.get());
				writeOut();
			}else if (tranRec.getKbn() == cs.KBN_UPDATE){
				editOutRec();
				writeOut();
			}else{
				System.out.println("処理区分が不正なので処理できません : " + tranRec.get());
				outRec.set(mastRec.get());
				writeOut();
			}
			readMast();
			readTran();
		}else if (mastRec.getId() > tranRec.getId()){
			//
			// トランザクションのみの場合(又は更新済みの場合)
			//
			if (tranRec.getKbn() == cs.KBN_DELETE){
				System.out.println("削除データが存在しないので削除できません : " + tranRec.get());
			}else if (tranRec.getKbn() == cs.KBN_INSERT){
				editOutRec();
				writeOut();
			}else if (tranRec.getKbn() == cs.KBN_UPDATE){
				System.out.println("更新データが存在しないので更新できません : " + tranRec.get());
			}else{
				System.out.println("処理区分が不正かつ対象データが存在しないので処理できません : " + tranRec.get());
			}
			readTran();
		}else{
			//
			// マスターのみの場合
			//
			outRec.set(mastRec.get());
			writeOut();
			readMast();
		}
		setEndSw();
	}
	//
	// 終了処理
	//
	void endProc() throws Exception {
		System.out.println(cs.PGMNAME + " : マスター更新処理 終了");
	}
	//
	// クローズ処理
	//
	void closeProc() throws Exception {
		mastFile.close();
		tranFile.close();
		outFile.close();
	}
	//
	// マスター読み込み
	//
	void readMast() throws Exception {
		if (mastRec.set(mastFile.read()) == null){
			mastRec.setId(Integer.MAX_VALUE);
		}
	}
	//
	// トランザクション読み込み
	//
	void readTran() throws Exception {
		if (tranRec.set(tranFile.read()) == null){
			tranRec.setId(Integer.MAX_VALUE);
		}
	}
	//
	// 更新後マスターファイル書き込み
	//
	void writeOut() throws Exception {
		outFile.write(outRec.get());
	}
	//
	// 終了判断
	//
	void setEndSw() throws Exception {
		if (mastRec.getId() == Integer.MAX_VALUE &&
		    tranRec.getId() == Integer.MAX_VALUE){
			endSw.on();
		}
	}
	//
	// 更新後マスターレコード編集
	//
	void editOutRec(){
		outRec.initialize();
		outRec.setId(tranRec.getId());
		outRec.setName(tranRec.getName());
		outRec.setJob(tranRec.getJob());
	}
}

CsvTest.java 実行結果(正常終了)

$ head -20 CsvTest_mast.txt CsvTest_tran.txt
==> CsvTest_mast.txt <==
10      氏名10  職業10
11      氏名11  職業11
20      氏名20  職業20
21      氏名21  職業21
30      氏名30  職業30
31      氏名31  職業31

==> CsvTest_tran.txt <==
2       10      氏名10T 職業10T
0       11      氏名11T 職業11T
0       15      氏名15T 職業15T
1       16      氏名16T 職業16T
2       17      氏名17T 職業17T
3       18      氏名18T 職業18T
2       21      氏名21T 職業21T
0       25      氏名25T 職業25T
1       26      氏名26T 職業26T
2       27      氏名27T 職業27T
1       30      氏名30T 職業30T
4       31      氏名31T 職業31T
$ java CsvTest
CsvTest : マスター更新処理 開始
CsvTest : 削除データが存在しないので削除できません : 0    15      氏名15T 職業15T
CsvTest : 更新データが存在しないので更新できません : 2    17      氏名17T 職業17T
CsvTest : 処理区分が不正かつ対象データが存在しないので処理できません : 3  18      氏名18T 職業18T
CsvTest : 削除データが存在しないので削除できません : 0    25      氏名25T 職業25T
CsvTest : 更新データが存在しないので更新できません : 2    27      氏名27T 職業27T
CsvTest : 既にデータが存在するので追加できません : 1      30      氏名30T 職業30T
CsvTest : 処理区分が不正なので処理できません : 4  31      氏名31T 職業31T
CsvTest : マスター更新処理 終了
$ head -20 CsvTest_out.txt
10      氏名10T 職業10T
16      氏名16T 職業16T
20      氏名20  職業20
21      氏名21T 職業21T
26      氏名26T 職業26T
30      氏名30  職業30
31      氏名31  職業31
$

CsvTest.java 実行結果(異常終了)

$ head -20 CsvTest_mast.txt CsvTest_tran.txt
==> CsvTest_mast.txt <==
10      氏名10  職業10
11      氏名11  職業11
20      氏名20  職業20
21      氏名21  職業21
30      氏名30  職業30
31      氏名31  職業31

==> CsvTest_tran.txt <==
2       10      氏名10T 職業10T
0       11      氏名11T 職業11T
0       15      氏名15T 職業15T
1       16      氏名16T 職業16T
2       17      氏名17T 職業17T
3       1B      氏名18T 職業18T
2       21      氏名21T 職業21T
0       25      氏名25T 職業25T
1       26      氏名26T 職業26T
2       27      氏名27T 職業27T
1       30      氏名30T 職業30T
4       31      氏名31T 職業31T
$ java CsvTest
CsvTest : マスター更新処理 開始
CsvTest : 削除データが存在しないので削除できません : 0    15      氏名15T 職業15T
CsvTest : 更新データが存在しないので更新できません : 2    17      氏名17T 職業17T
CsvTest : 例外エラーが発生したので強制終了します : java.lang.NumberFormatException: For input string: "1B"
$  head -20 CsvTest_out.txt
==> CsvTest_out.txt <==
10      氏名10T 職業10T
16      氏名16T 職業16T
$

2010年11月12日 Java儂(わし)的解釈によるメモ(データベース更新処理編) [長年日記]

_ Java儂(わし)的解釈によるメモ(データベース更新処理編)

マッチング処理編に続くデータベース更新編。

最初は軽いサンプルのつもりが、みるみる無駄に凝り出すCOBOLERオヤジがここに...。

SQLException.getSQLState()デッドロックを判断してrollbackした後に、commit済みのトランザクションは読み捨ててそこから自動的に再開みたいな芸当をしているが、DBMS非依存かどうかはチト微妙。

今回はスマートにまとまるかと思ったが結局ドロドロしてしまった。まぁ、ちょっとしたデータベースがらみ処理のスケルトンにできれば吉とするか。

動作検証はMicrosoft SQL Server 2008 Express Editionで行ったが、他のDBMSでもほんの一部の手直しで動くはず(?)。

TextFile.java

マッチング処理編で定義していたクラスと同じ物。

class TextFile { ... 省略 ... }

MasterRecord.java

マッチング処理編で定義していたクラスと同じ物。後述のテーブルクラスの親クラスとなる。

class MasterRecord { ... 省略 ... }

Database.java

JDBC経由でデータベースにアクセスする際に楽できるように作ったクラス。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
//
// データベースクラス
//
public class Database {
	Connection conn;
	String driver;
	String url;
	String user;
	String password;
	long commit_interval;
	long commit_count;
	int isolation_level;
	//
	// コンストラクタ
	//
	Database(String driver, String url, String user, String password) {
		this.conn = null;
		this.driver = driver;
		this.url = url;
		this.user = user;
		this.password = password;

		commit_interval = 1;
		commit_count = 0;
		isolation_level = -1;
	}
	//
	// データベース接続
	//
	void open() throws Exception {
		//
		// JDBCドライバーのロード
		//
		Class.forName(driver).newInstance();
		//
		// データベースへの接続
		//
		conn = DriverManager.getConnection(url, user, password);
		//
		// トランザクションレベル設定(ACIDのIを完全保証)
		//
		setIsolationSerializable();
		//
		// 自動コミット停止
		//
		setAutoCommitOff();
	}
	//
	// データベース切断
	//
	void close() throws Exception {
		conn.close();
	}
	//
	// データベースハンドラー取得
	//
	Connection getHandle(){
		return conn;
	}
	//
	// トランザクションレベル設定
	//
	//   矛盾の発生しないレベル
	//
	//     TRANSACTION_SERIALIZABLE
	//
	//   その他のレベル(何らかの矛盾が発生する可能性有り)
	//
	//     TRANSACTION_REPEATABLE_READ
	//     TRANSACTION_READ_COMMITTED(JDBCの既定値)
	//     TRANSACTION_READ_UNCOMMITTED
	//
	//   対応するSQL命令の例
	//
	//     SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
	//
	//   このコマンドの有効範囲はDBMSによってトランザクション単位であったり、
	//   接続(セッション)単位であったりまちまち。なので、小さい範囲の方のトラ
	//   ンザクションごとに発行するのが確実
	//
	//   多くのDBMS(MySQL, PostgreSQL, Oracle, Microsoft SQL Server)で確実に
	//   サポートされているのは
	//
	//     SERIALIZABLE
	//     READ COMMITTED
	//
	void setIsolation() throws Exception {
		conn.setTransactionIsolation(isolation_level);
	}
	//
	// トランザクションレベル設定(ACIDのIを完全保証)
	//
	void setIsolationSerializable() throws Exception {
		isolation_level = conn.TRANSACTION_SERIALIZABLE;
		setIsolation();
	}
	//
	// トランザクションレベル設定(phantom-read, non-repeatable-readが発生の可能性有り)
	//
	void setIsolationReadCommitted() throws Exception {
		isolation_level = conn.TRANSACTION_READ_COMMITTED;
		setIsolation();
	}
	//
	// 自動コミット開始
	//
	void setAutoCommitOn() throws Exception {
		conn.setAutoCommit(true);
	}
	//
	// 自動コミット停止
	//
	void setAutoCommitOff() throws Exception {
		conn.setAutoCommit(false);
	}
	//
	// コミット間隔設定
	//
	void setCommitInterval(long interval){
		commit_interval = interval;
	}
	//
	// コミット間隔リセット
	//
	void resetCommitInterval(){
		commit_interval = 1;
	}
	//
	// コミットカウンタリセット
	//
	void resetCommitCount(){
		commit_count = 0;
	}
	//
	// コミット
	//
	boolean commit() throws Exception {
		commit_count++;
		if ((commit_count % commit_interval) == 0){
			conn.commit();
			setIsolation();
			return true;
		}else{
			return false;
		}
	}
	//
	// ロールバック
	//
	void rollback() throws Exception {
		conn.rollback();
		setIsolation();
	}
	//
	// デッドロックの判定
	//
	//   SQLSTATEはSQL99にて定義されているとはいうもののDMBS方言もあるかも...
	//
	boolean is_deadlock(SQLException e){
		return is_deadlock(e.getSQLState());
	}
	boolean is_deadlock(String sqlstate){
		if ("40001".equals(sqlstate)){
			return true;
		}else{
			return false;
		}
	}
	//
	// 2重キーの判定
	//
	//   SQLSTATEはSQL99にて定義されているとはいうもののDMBS方言もあるかも...
	//
	//   最近(2014.03.xx)に気づいた事
       //     PostgreSQLでは2重キーを発生させた場合rollbackしないといけない :(
	//
	boolean is_dupkey(SQLException e){
		return is_dupkey(e.getSQLState());
	}
	boolean is_dupkey(String sqlstate){
		if ("23000".equals(sqlstate)){
			return true;
		}else{
			return false;
		}
	}
}

MasterTable.java

テーブルクラス定義。MasterRecordクラスを継承。

テーブルに関するアクセス処理は、基本的にこのクラス内のメソッドに閉じ込める。

import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
//
// テーブルクラス
//
//   業務テーブルのレイアウト毎に色々と定義されるであろうクラス
//
class MasterTable extends MasterRecord {
	PreparedStatement psDt;
	PreparedStatement psCt;
	PreparedStatement psDi;
	PreparedStatement psCi;
	PreparedStatement psIns;
	PreparedStatement psDel;
	PreparedStatement psUpd;
	PreparedStatement psSelId;
	ResultSet rsSelId;
	//
	// コンストラクタ(生成時の初期化処理)
	//
	MasterTable(Database db) throws Exception {
		psDt = db.getHandle().prepareStatement("drop table t_master");
		psCt = db.getHandle().prepareStatement(
			"create table t_master (" +
				"id INT PRIMARY KEY," +
				"name VARCHAR(50)," +
				"job VARCHAR(50)" +
			")"
		);
		psDi = db.getHandle().prepareStatement("drop index i_name on t_master");
		psCi = db.getHandle().prepareStatement(
			"create index i_name on t_master(" +
				"name" +
			")"
		);
		psIns = db.getHandle().prepareStatement("insert into t_master (id, name, job) values (?, ?, ?)");
		psDel = db.getHandle().prepareStatement("delete from t_master where id = ?");
		psUpd = db.getHandle().prepareStatement("update t_master set id = ?, name = ?, job = ? where id = ?");
		psSelId = db.getHandle().prepareStatement("select * from t_master where id = ?");
	}
	//
	// テーブル作成
	//
	int create() throws Exception {
		return psCt.executeUpdate();
	}
	//
	// テーブル削除
	//
	int drop() throws Exception {
		return psDt.executeUpdate();
	}
	//
	// インデックス作成
	//
	int createIndex() throws Exception {
		return psCi.executeUpdate();
	}
	//
	// インデックス削除
	//
	int dropIndex() throws Exception {
		return psDi.executeUpdate();
	}
	//
	// 追加
	//
	int insert() throws Exception {
		psIns.setInt(1, id);
		psIns.setString(2, name);
		psIns.setString(3, job);
		return psIns.executeUpdate();
	}
	//
	// 削除
	//
	int delete() throws Exception {
		psDel.setInt(1, id);
		return psDel.executeUpdate();
	}
	//
	// 更新
	//
	int update() throws Exception {
		psUpd.setInt(1, id);
		psUpd.setString(2, name);
		psUpd.setString(3, job);
		psUpd.setInt(4, id);
		return psUpd.executeUpdate();
	}
	//
	// 選択の条件設定(id)
	//
	ResultSet selectId(int id) throws Exception {
		psSelId.setInt(1, id);
		rsSelId = psSelId.executeQuery();
		return rsSelId;
	}
	//
	// 選択の実行(id)
	//
	boolean getSelectId() throws Exception {
		if (rsSelId.next()){
			setId(rsSelId.getInt("id"));
			setName(rsSelId.getString("name"));
			setJob(rsSelId.getString("job"));
			return true;
		}else{
			return false;
		}
	}
}

TableTest.java

//
// マスターテーブルをトランザクションファイルで更新する処理(JDBC)
//
//      環境変数
//              TRANFILENAME
//              COMMITFILENAME
//              ENCODING
//
//      トランザクション(入力) TAB区切りファイル
//              int     kbn     0:削除, 1:追加, 2:修正
//              int     id
//              String  name
//              String  job
//
//      コミット記録ファイル(出力) TAB区切りファイル
//              long    count
//              int     kbn
//              int     id
//              String  name
//              String  job
//
//      マスターテーブル(Microsoft SQL Server)
//              INT          id
//              VARCHAR(50)  name
//              VARCHAR(50)  job
//
import java.io.*;
import java.sql.*;

//
// 定数クラス
//
class Constant {
	// メッセージの先頭に付加するためのプログラム名称
	String PGMNAME = "TableTest";

	// JDBC関係のパラメータ
	String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	String URL = "jdbc:sqlserver://192.168.0.1:1433;databaseName=testdb";
	String USER = "sa";
	String PASSWORD = "system";

	// トランザクションの処理区分
        int KBN_DELETE = 0;
        int KBN_INSERT = 1;
        int KBN_UPDATE = 2;

	// 実コミット間隔、途中経過表示間隔
        int COMMIT_INTERVAL = 10;
        int SHOW_STATUS_INTERVAL = 10;
}
//
// トランザクションレコードクラス
//
class MasterRecordTran extends MasterRecord {
	int kbn;	// 更新区分

	MasterRecordTran() {
		kbn = 0;
	}

	void initialize() {
		super.initialize();
		kbn = 0;
	}

	String set(String record) {
		if (record != null){
			String[] array = record.split("\t", 2);
			kbn = Integer.valueOf(array[0]);
			super.set(array[1]);
		}
		return record;
	}

	String get() {
		return String.format("%d\t%s", kbn, super.get());
	}

	int getKbn() {
		return kbn;
	}

	void setKbn(int kbn) {
		this.kbn = kbn;
	}
}
//
// コミットレコードクラス
//
class CommitRecord extends MasterRecordTran {
	long count;	// コミット時のトランザクション件数

	CommitRecord() {
		count = 0;
	}

	void initialize(){
		super.initialize();
		count = 0;
	}

	String set(String record) {
		if (record != null){
			String[] array = record.split("\t", 2);
			count = Long.valueOf(array[0]);
			super.set(array[1]);
		}
		return record;
	}

	String get() {
		return String.format("%d\t%s", count, super.get());
	}

	long getCount() {
		return count;
	}

	void setCount(long conut) {
		this.count = count;
	}
}
//
// スイッチクラス
//
class Switch {
	boolean state;
	Switch(){
		state = false;
	}
	Switch(boolean state){
		this.state = state;
	}
	void on(){
		state = true;
	}
	void off(){
		state = false;
	}
	boolean is_on(){
		return (state == true);
	}
	boolean is_off(){
		return (state == false);
	}
}
//
// 引数パラメータクラス
//
class ExtParam {
	String tranFileName = "TableTest_tran.txt";	// トランザクションファイル名
	String commitFileName = "TableTest_commit.txt";	// コミットファイル名
	String encoding = "EUC-JP";	// エンコーディング名

	void getFromEnviron() {
		String env;

		if ((env = System.getenv("TRANFILENAME")) != null){
			tranFileName = env;
		}
		if ((env = System.getenv("COMMITFILENAME")) != null){
			commitFileName = env;
		}
		if ((env = System.getenv("ENCODING")) != null){
			encoding = env;
		}
	}

	String getTranFileName() {
		return tranFileName;
	}

	String getCommitFileName() {
		return commitFileName;
	}

	String getEncoding() {
		return encoding;
	}
}
//
// メインクラス
//
class TableTest {
	//--------------------------------------------------
	// 定数オブジェクト
	//--------------------------------------------------
	Constant cs = new Constant();

	//--------------------------------------------------
	// 引数オブジェクト
	//--------------------------------------------------
	ExtParam ep = new ExtParam();

	//--------------------------------------------------
	// 入出力ファイル
	//--------------------------------------------------
	//
	// トランザクションファイル&レコードオブジェクト
	//
	TextFile tranFile = new TextFile();
	MasterRecordTran tranRec = new MasterRecordTran();
	//
	// コミット記録ファイル&レコードオブジェクト
	//
	TextFile commitFile = new TextFile();
	CommitRecord commitRec = new CommitRecord();

	//--------------------------------------------------
	// データベース
	//--------------------------------------------------
	//
	// データベースオブジェクト
	//
	Database db = new Database(cs.DRIVER, cs.URL, cs.USER, cs.PASSWORD);
	//
	// マスターテーブルオブジェクト
	//
	MasterTable mt = null;

	//--------------------------------------------------
	// 各種スイッチ
	//--------------------------------------------------
	//
	// 処理終了スイッチ
	//
	Switch endSw = new Switch();
	//
	// アボートスイッチ
	//
	Switch abortSw = new Switch();
	//
	// デッドロックスイッチ
	//
	Switch deadlockSw = new Switch();

	//==================================================
	// メイン処理
	//==================================================
	public static void main(String[] args) throws Exception {
		TableTest m = new TableTest();
		try {
			m.openProc();		// オープン処理
			m.initProc();		// 初期処理
		}catch (Exception e){
			m.trapException(e);	// 一般例外処理
		}

 		while (m.endSw.is_off() && m.abortSw.is_off()){
			try {
				m.mainProc();	// 主処理
				if (m.deadlockSw.is_on()){
					m.retryDeadlock();
				}
			}catch (Exception e){
				m.trapException(e);	// 一般例外処理
			}
		}

		try {
			m.endProc();		// 終了処理
		}catch (Exception e){
			m.trapException(e);	// 一般例外処理
		}

		m.closeProc();		// クローズ処理
	}
	//==================================================
	// オープン処理
	//==================================================
	void openProc() throws Exception {
		// 環境変数取得
		ep.getFromEnviron();

		// データベースオープン
		db.open();
		mt = new MasterTable(db);

		// ファイルオープン
		tranFile.open(ep.getTranFileName(), "r", ep.getEncoding());
	}
	//==================================================
	// 初期処理
	//==================================================
	void initProc() throws Exception {
		System.out.println(cs.PGMNAME + " : マスターテーブル更新処理 開始");
//
//		db.setAutoCommitOn();
//		try {
//			mt.dropIndex();
//		}catch (Exception e){
//			System.err.println(cs.PGMNAME + " : インデックスを削除出来ません。このエラーは無視して処理を続行します。 : " +e);
//		}
//		try {
//			mt.drop();
//		}catch (Exception e){
//			System.err.println(cs.PGMNAME + " : テーブルを削除出来ません。このエラーは無視して処理を続行します。 : " +e);
//		}
//		mt.create();
//		mt.createIndex();
//		db.setAutoCommitOff();
//

		// コミット間隔設定
		db.setCommitInterval(cs.COMMIT_INTERVAL);

		// トランザクションの1件目読み込み
		readTran();
		showStatus();
	}
	//==================================================
	// 主処理
	//==================================================
	void mainProc() throws Exception {
		if (tranRec.getKbn() == cs.KBN_DELETE){
			// 削除処理
			doDelete();
		}else if (tranRec.getKbn() == cs.KBN_INSERT){
			// 追加処理
			doInsert();
		}else if (tranRec.getKbn() == cs.KBN_UPDATE){
			// 更新処理
			doUpdate();
		}else{
			System.err.println(cs.PGMNAME + " : 処理区分が不正なので処理できません : " + tranRec.get());
		}

		// コミット処理
		if (db.commit()){
			writeCommit();
		}

		// 次トランザクション読み込み
		readTran();
		showStatus();
	}
	//==================================================
	// 終了処理
	//==================================================
	void endProc() throws Exception {
		System.out.println(cs.PGMNAME + " : マスターテーブル更新処理 " + (abortSw.is_on() ? "異常" : "正常") + "終了");
	}
	//==================================================
	// クローズ処理
	//==================================================
	void closeProc() throws Exception {
		// 最終のロールバック or コミット
		if (abortSw.is_on()){
			db.rollback();
		}else{
			db.resetCommitInterval();
			db.commit();
			deleteCommit();
		}

		// データベースクローズ
		db.close();

		// ファイルクローズ
		tranFile.close();
	}
	//==================================================
	// デッドロックリトライ処理
	//==================================================
	void retryDeadlock() throws Exception {
		System.err.println(cs.PGMNAME + " : " + tranFile.getReadCount() + "件目のトランザクションでデッドロックが発生しました");
		db.rollback();
		db.resetCommitCount();
		reReadTran();
		deadlockSw.off();
		System.err.println(cs.PGMNAME + " : " + tranFile.getReadCount() + "件目のトランザクションからリトライします");
	}
	//==================================================
	// Exceptionハンドリング処理
	//==================================================
	void trapException(Exception e) throws Exception {
		System.err.println(cs.PGMNAME + " : 例外エラーが発生したので強制終了します : " + e);
		abortSw.on();
	}
	//--------------------------------------------------
	// トランザクション読み込み
	//--------------------------------------------------
	void readTran() throws Exception {
		if (tranRec.set(tranFile.read()) == null){
			endSw.on();
		}
	}
	//--------------------------------------------------
	// 途中経過(トランザクション件数)表示
	//--------------------------------------------------
	void showStatus() throws Exception {
		if (endSw.is_off()){
			if ((tranFile.getReadCount() % cs.SHOW_STATUS_INTERVAL) == 0){
				System.out.println(cs.PGMNAME + " : トランザクション読み込み件数 = " + tranFile.getReadCount());
			}
		}
	}
	//--------------------------------------------------
	// トランザクション読み込み直し
	//--------------------------------------------------
	void reReadTran() throws Exception {
		long max_count;
		long count;

		// コッミットファイルからコミット済みトランザクション件数取得
		readCommit();
		max_count = commitRec.getCount();

		// トランザクションファイル読み飛ばし
		tranFile.close();
		tranFile.open(ep.getTranFileName(), "r", ep.getEncoding());
		for (count = 0; count < max_count; count++){
			readTran();
		}
		readTran();
	}
	//--------------------------------------------------
	// コミット時のトランザクション(最終の1件)を記録
	//--------------------------------------------------
	void writeCommit() throws Exception {
		commitFile.open(ep.getCommitFileName(), "w", ep.getEncoding());
		commitRec.set(Long.toString(tranFile.getReadCount()) + "\t" + tranRec.get());
		commitFile.write(commitRec.get());
		commitFile.flush();
		commitFile.close();
	}
	//--------------------------------------------------
	// コミットファイル読み込み
	//--------------------------------------------------
	void readCommit() throws Exception {
		try {
			commitFile.open(ep.getCommitFileName(), "r", ep.getEncoding());
			if (commitRec.set(commitFile.read()) == null){
				commitRec.initialize();
			}
			commitFile.close();
		}catch (FileNotFoundException e){
			commitRec.initialize();
		}
	}
	//--------------------------------------------------
	// コミットファイル削除
	//--------------------------------------------------
	void deleteCommit() throws Exception {
		File f = new File(ep.getCommitFileName());
		f.delete();
	}
	//--------------------------------------------------
	// 削除処理
	//--------------------------------------------------
	void doDelete() throws Exception {
		try {
			mt.setId(tranRec.getId());
			if (mt.delete() == 0){
				System.err.println(cs.PGMNAME + " : 削除データが存在しないので削除できません : " + tranRec.get());
			}
		}catch (SQLException e){
			if (db.is_deadlock(e)){
				deadlockSw.on();
			}else{
				System.err.println(cs.PGMNAME + " : 削除処理でSQL例外エラーが発生したので強制終了します : " + e + " : " + e.getSQLState());
				abortSw.on();
			}
		}
	}
	//--------------------------------------------------
	// 追加処理
	//--------------------------------------------------
	void doInsert() throws Exception {
		try {
			mt.setId(tranRec.getId());
			mt.setName(tranRec.getName());
			mt.setJob(tranRec.getJob());
			if (mt.insert() == 0){
				System.err.println(cs.PGMNAME + " : 追加できません : " + tranRec.get());
			}
		}catch (SQLException e){
			if (db.is_deadlock(e)){
				deadlockSw.on();
			}else if (db.is_dupkey(e)){
				System.err.println(cs.PGMNAME + " : 既にデータが存在するので追加できません : " + tranRec.get());
			}else{
				System.err.println(cs.PGMNAME + " : 追加処理でSQL例外エラーが発生したので強制終了します : " + e + " : " + e.getSQLState());
				abortSw.on();
			}
		}
	}
	//--------------------------------------------------
	// 更新処理
	//--------------------------------------------------
	void doUpdate() throws Exception {
		try {
			mt.setId(tranRec.getId());
			mt.setName(tranRec.getName());
			mt.setJob(tranRec.getJob());
			if (mt.update() == 0){
				System.err.println(cs.PGMNAME + " : 更新データが存在しないので更新できません : " + tranRec.get());
			}
		}catch (SQLException e){
			if (db.is_deadlock(e)){
				deadlockSw.on();
			}else{
				System.err.println(cs.PGMNAME + " : 更新処理でSQL例外エラーが発生したので強制終了します : " + e + " : " + e.getSQLState());
				abortSw.on();
			}
		}
	}
}

2010年11月29日 データベースの排他制御に関して [長年日記]

_ トランザクション分離レベルはSERIALIZABLEが望ましい by 名も無きCOBOLER

多くのRDBMSではトランザクション分離レベルとSELECT時の排他指定の組み合わせによって、排他制御の挙動にいくつかのパターンがある。

トランザクション分離レベルに関しては一般的に以下の4種類がある。

READ UNCOMMITTED
読み込むデータがコミット前のものである可能性がある。
READ COMMITTED
読み込むデータはコミット済みのものである事が保証される。
REPEATABLE READ
一度読み込んだデータは同一トランザクション内では何度読み直しても内容が変わらないことが保証される。
SERIALIZABLE
同一トランザクション内では同一条件で何度読み直しても検索結果のデータ集合が変わらないことが保証される。

これらの4種が全てのRDMBSで実装されているわけではなく、例えば商用RDBMSの代表としてOracleMicrosoft SQL Server、オープンソースの代表としてPostgreSQLMySQLで共通して指定できるのは、

  • READ COMMITTED
  • SERIALIZABLE

の2種である。

READ COMMITTED

の場合、Dirty readが排除され、

  • 先行トランザクション中で更新(Insert, Delete, Update)されたデータに対する、後続トランザクションからの全てのアクセス(Select, Insert, Delete, Update)は、先行トランザクションが完了(Commit, Rollback)するまで待たされる。
  • 先行トランザクション中で参照(Select)されたデータに対する、後続トランザクションからの全てのアクセス(Select, Insert, Delete, Update)は直ちに(先行トランザクションの完了を待たずに)実行される。

SERIALIZABLE

の場合、READ COMMITTEDの場合と同様にDirty readが排除され、

  • READ COMMITTEDの場合と同様に、先行トランザクション中で更新(Insert, Delete, Update)されたデータに対する、後続トランザクションからの全てのアクセス(Select, Insert, Delete, Update)は、先行トランザクションが完了(Commit, Rollback)するまで待たされる。
  • 先行トランザクション中で参照(Select)されたデータに対する、後続トランザクションからの更新アクセス(Insert, Delete, Update)は、先行トランザクションが完了(Commit, Rollback)するまで待たされる。
  • 先行トランザクション中で参照(Select)されたデータに対する、後続トランザクションからの参照アクセス(Select)は直ちに(先行トランザクションの完了を待たずに)実行される。

結果として、Phantom read、Non-repeatable readが排除される。

参考
Dirty read
コミット前(未確定)のデータを読み込んでしまう現象
Non-repeatable read
同一トランザクション内において、一度読み込んだデータを再度読み込んだ時にその内容が変化している現象
Phantom read
同一トランザクション内において、一度は読み込めなかったデータが再度読み込んだ時には読めてしまう現象

私見

READ COMMITTEDとSERIALIZABLEを比較した場合、SERIALIZABLEの同時実行性能が低いことを欠点として取り上げることが多いようで、実際JDBCのデフォルトもSERIALIZABLEを避けてREAD COMMITTEDになっている。

これは、たとえば単一のテーブルを参照してその結果を返すというような単純かつ参照性能のみに重きを置いたシステム(検索エンジン的な)では妥当な選択と言える。

しかし、一般的に事務系処理の場合、1回のトランザクション中で複数の関連するテーブルを参照&更新し、その過程においても、またその結果においても一切の矛盾が許されないのが当然なので、たとえ同時実行性能を犠牲にしてもSERIALIZABLE以外に選択の余地は無いと考える。

もちろんこの場合、SQLの文法上は1回で実行できる更新命令であっても、あえて複数回のSQL文に分割して更新するといった工夫とともに、適切なタイミングでコッミット命令を実行し、トランザクションの粒度を下げる努力は必要になるだろう。

ちなみに

READ COMMITTEDの場合でも参照命令(SELECT)に排他指定を行うとSERIALIZABLEを指定したのと同様の動きとなる。

これは一見すると参照命令の書きようしだいでREAD COMMITTED的な動きかSERIALIZABLE的な動きを柔軟にコントロールできて良さそうに見えるのだが、本来トランザクション分離レベルなどというものはトランザクション内で(いやもっと言ってしまうとシステム全体で)あれやこれや選択するようなものでは無く、統一した状態であるべきだと考える。

結果としてREAD COMMITTED + 参照命令の排他指定でSERIALIZABLE的な効果を得ようとするのであれば、最初からトランザクション分離レベルをSERIALIZABLEに設定し、参照命令での排他指定を意識する必要の無い状態を選択するべきだろう。

これは余談だが、参照命令(SELECT)の排他指定の方法はRDBMSごとに方言(SELECT ... WITH (HOLDLOCK)とか、SELECT ... FOR UPDATEとか)が有り、その点においても積極的に利用する気にはなれない...。


2010年11月30日 ruby-1.9.xのエンコーディング関係のエラー [長年日記]

_ invalid multibyte char (US-ASCII)

たとえばeuc-jpで記述されたスクリプトの日本語がらみの部分でこのエラーが出た場合は、スプリプトの先頭を

#! /usr/bin/ruby
# -*- encoding: euc-jp -*-

のようにして、エンコーディングを指定する。

追記 : 上記のようにしてもダメなことがあった。`#! /usr/bin/ruby -Ke' と指定するとOKとなった。

_ incompatible character encodings: EUC-JP and ASCII-8BIT (Encoding::CompatibilityError)

たとえばeuc-jpで文字列が格納されているはずの変数strでこのエラーが出た場合は

str.force_encoding('euc-jp')

のようにしてエンコーディングを変更してやる。

実際には dbi, dbd-odbc辺りでこの現象が出た。以下のように対応した。

sql = <<SQL
        select * from member where id >= ?
SQL
sth = dbh.prepare(sql)
sth.execute(1)
while (row = sth.fetch)
        sth.column_names.each {|name|
                if (row[name].kind_of?(String))
                        row[name].force_encoding('euc-jp')
                end
        }
        puts "セレクトに成功しました(id=#{row['id']})(name=#{row['name']})(job=#{row['job']})"
end
sth.finish

いずれもruby-1.8系からruby-1.9系への変更による影響。