Going my way

いいなと思ったことをメモしていきます。

JavaからMySQLにSELECT文を投げるコードサンプル

MySQLサーバからJavaでデータを取得するためのサンプルを載せます。

まずはDBの情報。
以下のようなDBのデータをJavaを使って取得したい。

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(100) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
4 rows in set (0.03 sec)

中に入れるデータはこの通り。

insert into test1 values(1,'ONE PIECE','尾田栄一郎','海賊王に おれはなる!');
insert into test1 values(2,'HUNTER HUNTER','冨樫義博','カイトは生きてる!');
insert into test1 values(3,'ドラゴンボール','鳥山明','オラわくわくすっぞ!');

中を見てみよう。

mysql> select * from test1;
+----+----------------+------------+------------------------+
| id | title          | author     | sentence               |
+----+----------------+------------+------------------------+
|  1 | ONE PIECE      | 尾田栄一郎 | 海賊王に おれはなる! |
|  2 | HUNTER HUNTER  | 冨樫義博   | カイトは生きてる!     |
|  3 | ドラゴンボール | 鳥山明     | オラわくわくすっぞ!   |
+----+----------------+------------+------------------------+
3 rows in set (0.00 sec)

参考にしたのは下記のサイトだ。
http://dev.mysql.com/doc/refman/5.1/ja/connector-j-usagenotes-basic.html

properties.txt

jdbc=mysql
host=localhost
db=db1
user=test1
pass=test1
sql=select * from test1

MySQLSelect.java

package jdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.sql.*;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;

public class MySQLSelect {
	private static String jdbc = null;
	private static String db = null;
	private static String host = null;
	private static String user = null;
	private static String pass = null;
	private static String driver = null;
	private static String sql = null;
	
	
	public static void main(String[] args){
		Properties prop = new Properties();
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("properties.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println("プロパティファイルが見つかりません");
		}
		
		try {
			prop.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("プロパティファイルを開けません");
		}
		
		//プロパティの読み込み
		jdbc = prop.getProperty("jdbc");
		db = prop.getProperty("db");
		host = prop.getProperty("host");
		user = prop.getProperty("user");
		pass = prop.getProperty("pass");
		sql = prop.getProperty("sql");
		
		//ドライバの作成
		//jdbc:mysql://localhost/db1?user=test1&password=test1
		driver = "jdbc:"+jdbc+"://"+host+"/"+db+"?user="+user+"&"+"password="+pass;
		
		Connection con = null;
		try{
			con = (Connection) DriverManager.getConnection(driver);
			Statement stmt = (Statement) con.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()){
				int id = rs.getInt("id");
				String title = rs.getString("title");
				String author = rs.getString("author");
				String sentence = rs.getString("sentence");
				System.out.println(id+":【"+ title +"】\n" + author + "\n"+ sentence+"\n");
			}
		}catch(SQLException ex){
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		}finally{
			if(con != null){
				try{
					con.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
		
	}
}

実行すると変なエラーが出たので、修正を試みる。

SQLException: Access denied for user 'test1'@'localhost' (using password: YES)
SQLState: 28000
VendorError: 1045

以下の2つのサイトを参考に、パスワードや権限を再設定した。

http://blog.layer8.sh/ja/2011/12/21/%E3%80%8Cerror-1045-connect-failed-access-denied-for-userusing-password-yes%E3%80%8D%E3%81%AE%E5%AF%BE%E5%87%A6%E6%B3%95symfony/

http://sasuke.main.jp/useri.html

GRANT ALL PRIVILEGES ON *.* TO 'test1'@'localhost' IDENTIFIED BY 'test1'
or
GRANT ALL PRIVILEGES ON *.* TO 'test1'@'%' IDENTIFIED BY 'test1';

無事に実行ができるようになった。<実行結果>

1:【ONE PIECE】
尾田栄一郎
海賊王に おれはなる!

2:【HUNTER HUNTER】
冨樫義博
カイトは生きてる!

3:【ドラゴンボール】
鳥山明
オラわくわくすっぞ!

JavaEE,Tomcatの設定等の周辺知識のまとめ

Javaを使ってWebアプリを作りたいときに、ぶち当たる壁。
それは環境構築の壁である。

URLを指定しても404エラーが出る。なぜ。
そもそもTomcatが起動しない。なぜだ。
weeb.xml,server.xml色々あって意味がわからない・・・。なんなんだ。

そんなとき、いつものようにgoogleを使って検索を試みるけれど、
基礎知識もないのに断片的な情報をネットで拾っても問題は解決しない。

webをさまよった後、僕は悟る。
googleは万能ではないと。


基礎に立ち戻り、本を読んだ。
その結果をまとめたのがこの記事である。


以下、書籍のまとめです。
詳しく知りたい人はこちらの本を読みましょう。


webアプリケーションとは
サーバ側で動作するアプリケーションのこと。
Java仕様書によれば、Webアプリケーションとは、サーブレットJSP、HTMLドキュメント、その他イメージファイル、圧縮されたアーカイブなどを含むリソース全体を集めてきたもの。

JavaEEとは
サーバ再度アプリケーションを実現する包括的な仕様のこと。
Enterprise Editionの略。
ちなみにJavaSEとは、Java Standard Editionのことであり、
JVM,JNDI,JDBC,Swingなど、JVM、標準API、デスクトップ環境APIを含むエディションのこと。

コンテナとは
サーバー側でWebアプリケーションを動作させるための実行環境のこと。
サーブレットの実行環境はWebコンテナ、JSPの実行環境はJSPコンテナという。
Webコンテナ、JSPコンテナを総称してWebコンテナということもある。

Tomcatとは
サーブレット/JSPの仕様に基づいて実装されたWebコンテナのこと。
Http通信処理機能もあるため、Webサーバとしても利用できる。

CATALINA_HOMEとは
Tomcatのルートのこと。
Tomcatでは、%CATALINA_HOME%\webapps以下にコンテキストルートを置くことが決められている。
コンテキストルートについては後述する。


Tomcatのインストール手順

linuxにインストールする場合
手順書.comが一番わかりやすかったです。
http://tejunsho.com/tomcat/20110331000102.html

ただし、この手順書の通り実行しても下記のエラーが出るため、
5のパッケージがなかったので「6」に変更。

(誤)#yum install tomcat5 tomcat5-webapps

エラーメッセージ

Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
 * epel: ftp.kddilabs.jp
Setting up Install Process
No package tomcat5 available.
No package tomcat5-webapps available.
Error: Nothing to do

正しいバージョン

(正)yum install tomcat6 tomcat6-webapps

JavaEE仕様の標準ディレクトリ構成(重要)

コンテキストルート
    │
    ├WEB-INF
    │    │
    │    ├classes
    HTML ├src
    JSP   ├web.xml
	      ├lib
	      │ ├JAR
	      │
          ├tags
          


繰り返しになるが、ディレクトリ構成は仕様として決められている。

コンテキストルート
→Webアプリケーションを構成するすべてのコンテンツを格納するディレクトリのこと。
 webapps直下のディレクトリ。

WEB-INF
→コンテキストルート以下に必ず配置しなければいけないディレクトリ。
Javaファイルや設定ファイルを格納する。このディレクトリ以下はインターネットでは公開されない。
必ず大文字半角で記述する。


classes
Javaのクラスファイルを格納するディレクトリ。コンテキストルート\WEB-INF以下に配置する。
パッケージ化したクラスはパッケージに対応したディレクトリで格納する。
例)jp.co.hoge.Testクラスなら
WEB-INF/classes/jp/co/hoge/Test.classの階層で格納する。


lib
→JARファイルを格納する。ここに格納されたファイルは、Webコンテナ起動時に自動的にクラスパスが設定される。
JDBCなどはここに配置する。

src
→意外にも、これは任意のフォルダ。あってもなくてもいい。一般にソースファイルを置く。


web.xmlとは
webアプリケーションの設定ファイルのこと。配備記述子(デプロイメントディスクリプタ)と呼ばれる。
/WEB-INF/web.xml
に置く。繰り返すが、ディレクトリ構造はあらかじめサーブレットAPI仕様書によって決められている。

サーブレットの登録
web.xmlに登録されたサーブレットの呼び出しのための構文。
以下のURL構文で呼び出す。

http://<ホスト名>:<ポート番号>/<コンテキストルート名>/

web.xmlの記述が下記の場合・・・

  <servlet>
    <description></description>
    <display-name>Sample1_1</display-name>
    <servlet-name>Sample1_1</servlet-name>
    <servlet-class>servlet.Sample1_1</servlet-class> ★サーブレットクラス名
  </servlet>
  <servlet-mapping>
    <servlet-name>Sample1_1</servlet-name> ★マッピングするURLパターン
    <url-pattern>/Sample1_1</url-pattern>
  </servlet-mapping>

サーブレット呼び出しの構文は以下の通り
http://localhost:8082/webComp/Sample1_1
tomcatのデフォルトのポートは8080なので注意が必要。
※ポートはserver.xmlで設定可能です。

マッピングするパターンを変更すると・・・

  <servlet>
    <description></description>
    <display-name>Sample1_1</display-name> 
    <servlet-name>Sample1_1</servlet-name>
    <servlet-class>servlet.Sample1_1</servlet-class> ★サーブレットクラス名
  </servlet>
  <servlet-mapping>
    <servlet-name>Sample1_1</servlet-name>
    <url-pattern>/hoge</url-pattern> ★マッピングするパターンを変更すると・・・・
  </servlet-mapping>

呼び出しURLは下記のようになる。
http://localhost:8082/webComp/hoge

は一致させること。
拡張子を指定することはできない。

ウェルカムページの設定

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

http://localhost:8082/webComp/
のようにコンテキストルートまでしかURLで指定しないとき、上から順番にに書かれたファイルを検索して表示する。

HTTPのステータスコードによってファイルを指定する場合

  <error-page>
    <error-code>404</error-code>
    <location>/sorry.html</location>
   </error-page>

このように記述しておくと、404(ファイルが見つからない)ときは、コンテキストルート以下のsorry.htmlが表示される。

タグ
web.xmlのルートとなるタグ

tomcat上で「コンテキストルート」とは、webapps直下のディレクトリである。

とりあえずのまとめでした。
随時、追記します。
いやほんと、tomcatの設定って訳わからんかったし、検索してもなかなか解決しなくて大変だった。

基本過ぎていまさら聞けないJavaの盲点集

インターフェースは多重継承ができる

Javaではクラスの多重継承は禁止されているが、インターフェースの多重継承は可能である。
インターフェース内で変数の宣言はできない。フィールドは全て定数になる。

(例)

package test;

interface Man{
	public static final String He = "男";
	public abstract void Tinko();
}

interface Woman{
	//interface内の変数は暗黙的にpublic static finalとなる。定数の初期化は必須である。
	String She = "女";
	
	//インターフェースのメソッドは暗黙的にpublic abstraceで就職される
	void Manko();
}

interface Okama extends Man, Woman{
	
}

/** おかまの能力を継承した新人類クラス */
public class Shinjinrui implements Okama{

	public void Tinko(){
		System.out.println("男性のもの");
	}
	//このメソッドのアクセス修飾子はpublic じゃないとエラーになる。
	//なぜならインターフェースのメソッドが暗黙的にpublic abstractで修飾されているから
	//アクセス修飾子を厳しくすることはできない。
	public void Manko(){
		System.out.println("女性のもの");
	}
	
}
package test;

public class Main {
	public static void main(String[] args){
		Shinjinrui kami = new Shinjinrui();
		kami.Tinko();
		kami.Manko();
		System.out.println(kami.He);
		System.out.println(kami.She);
	}
}

<結果>


男性のもの
女性のもの
男
女

Stringクラス(不変)とStringBuilderクラス(可変)の違い
Stringクラスは不変として設計されているため、Stringオブジェクトがラップする文字列の変更するためのメソッドは聴許されていない。

変更可能な文字列を作成したい場合は、StringBufferクラスを使う必要がある。

Stringクラスで提供されている文字列連結のためのメソッドはconcat
StringBuilderクラスの場合はappendメソッドを使う。

public class StringTest {
	public static void main(String[] args){
		String str = "original";
		String strTmp = null;
		
		//こうすると新しいオブジェクトが生成されるが、ここでこのオブジェクトを参照する変数がないからガーベジコレクションの対象になる
		str.concat(" addStr");
		
		strTmp = str.concat(" addStr");
		
		//結果は「strTmp:original addStr」
		System.out.println("strTmp:" +strTmp);
		
		//結果は「str.concat():original」
		System.out.println("str.concat():"+str);
		
		//新たにできた「original addStr2」のオブジェクトを参照する
		str = str.concat(" addStr2");
		
		//結果は「str.concat():originaladdStr」
		System.out.println("str.concat():"+str);
		
		
		StringBuffer strb = new StringBuffer("originalBuff");
		strb.append("addBuff");
		System.out.println(strb);
		
	}
}

「equals」と「==」の違いについて
以下のようにString型の変数を宣言する。

String str = "hoge";

これを宣言したとき、同じ"hoge"のリテラル文字列が既にメモリ上にあれば、

そのリテラル文字列が変数に割り当てられる。

str2 = "hoge"とした場合

以下の図のように、同じオブジェクトを参照することになる。
f:id:go_my_own_way:20120718182934g:plain

「==」は同じオブジェクトを参照するかを比較する。
「equals」はオブジェクトが保持する文字列を比較する。

以下の例を見ればよりわかりやすいと思う。

public class StringTest2 {
	public static void main(String[] args){
		String str1 = "hoge";
		String str2 = "hoge";
		
		//結果はTRUE
		if (str1 == str2){
			System.out.println("str1とstr2は同じオブジェクトを参照している");
		}else{
			System.out.println("str1とstr2は違うオブジェクトを参照している");
		}
		
		
		//新たにnewでオブジェクトを作った場合
		String str3 = "hoge";
		String str4 = new String("hoge");
		
		//結果はFALSE
		if (str3 == str4){
			System.out.println("str3とstr4は同じオブジェクトを参照している");
		}else{
			System.out.println("str3とstr4は違うオブジェクトを参照している");
		}
		
		
		//equalsの場合、結果はTRUE
		if (str3.equals(str4)){
			System.out.println("str3とstr4のオブジェクトは同じ文字列を保持している");
		}else{
			System.out.println("str3とstr4のオブジェクトは同じ文字列を保持している");
		}
		
		
	}
}


<結果>

str1とstr2は同じオブジェクトを参照している
str3とstr4は違うオブジェクトを参照している
str3とstr4のオブジェクトは同じ文字列を保持している


ちゃんと詳しく勉強したい人がいたら、この本を参考にするといいです。

「成金」それはホリエモンの成り上がりを追体験できるもの

成金
成金
posted with amazlet at 12.07.17
堀江 貴文
徳間書店
売り上げランキング: 70332

ご存知「ホリエ本」。

本作は「拝金」に続くホリエモンの二作目の小説である。

文章自体はケータイ小説のように軽い。
話の展開も村上春樹のようにファンタスティックでもなく、
伊坂幸太郎のように張り巡らされた伏線が気持よく回収される訳でもない。

それでも、面白い。

それはなぜか。
堀江貴文という男の人生が魅力的だからだ。
文章は軽くても、堀江の人生が濃く重いからだ。
そして、実在する人物(それは容易に想像できる)とのやりとり。
フィクションの中の圧倒的なリアリティこそが、この小説の魅力だ。

人の人生を形容する言葉として、
「ジェットコースター」
という言葉がこれほど似合う男はいないだろう。

IT業界の寵児ともてはやされた2000年代前半。
それが一転し、2006年ライブドア事件での逮捕。
釈放されてからは、出版やブログ、メルマガを通じて活動を再開。
ひと月840円のメルマガの読者数は1万人を超えた。
東日本大震災時には50万人を超えるフォロワーに対し、行方不明者の情報をリツイートし、それによって救われた人も現れた。
そして2011年4月26日、2年の実刑判決が下された。

まさにジェットコースターではないか。
面白い。不謹慎かもしれないが、本当に面白い人だと思う。

この「成金」という小説の中には、堀江貴文からのメッセージが所々に散りばめられている。
小説の展開も所々現実とリンクしているが、ここでは堀江貴文からのメッセージを小説の中の言葉のままに紹介したい。

「金で動かされる人間はダメだ。価値のあるのは、ま、使える人間って言い換えてもいいけど、金でしか動かない本当のプロフェッショナルか、金では動かないアマチュアなんだよ」 

IT業界はスピードが全てだ。 
堀井は確信している。 
構想しているアイデアは間違いなく画期的だが、いま、この瞬間、まったく同じアイデアを持った人間が必ずどこかにいる。競争相手はインターネットで網羅された全世界なのだ。ライバルは見たことも聞いたこともないどこかの国の、小さな街角の、ぼろいガレージの中にいる。 

自由な意思がなければ、ITの世界を勝ち抜くのは不可能だ。 

「信じ切ることだ。僕たちならできる、その先には必ず未来が開かれている。絶対に世界で勝てる。僕たちが最も優れている」 

(パソコンを指して)この小さな箱には無限の世界が広がっている。 

起業は傍目にはかっこよく映るかもしれない。でもその資格は、死ぬほど寒い真冬の夜に、室外機のぬくもりを頼りに平然と寝る覚悟があるやつだけに与えられる。 

値段をつけられることではなく、値段をつける側になるほうが、ずいぶんましなことだし、もっと言えば、本当の価値とは誰からも値段をつけられないことだと、僕はそう思うけどね。 

国内のみならず、世界の主要なIT企業はぼろくて狭い空間(ガレージ)で産声を上げてきた。 
ビル・ゲイツのマイクロソフト、スティーブ・ジョブズのアップル。世界に冠絶するIT企業のトップたちはその風景を知っている。ほかならず彼ら自身がほんの数十年前にガレージで立ち上げたのだから。 
堀井は、それがITの可能性だと思っている。 

「すべてはガレージから----」 
このぼろい事務所こそがITの持つ可能性だった。夢そのものなのだ。 

この本は、堀江貴文が駆け上がって行く時の、当時の息吹を感じさせてくれる。
私達は、成功した後の彼しか知らないが、彼の雌伏の時期にこそ、彼の魅力が詰まっているのではないだろうか。
そして、稀代の起業家である堀江の雌伏の時期を追体験できる最も有効な方法は、この小説を読むことだったのだ。



成金
成金
posted with amazlet at 12.07.17
堀江 貴文
徳間書店
売り上げランキング: 70332

いまさら聞けない基本過ぎて見逃しがちなJavaの基礎の基礎

Javaプログラマの勉強をしていて、実は基礎の基礎の部分の知識が曖昧だったことに気付いた。
ここで、今まで曖昧にしていた知識を一旦まとめてみようと思う。


■データ型について

データ型は、「基本データ型」と「参照型」に分かれている。
数値や文字などを意味するデータ型のことを基本データ型またはプリミティブ型という。

リテラルとは、ソースファイル内に直接記述した値のこと。

文字リテラル:文字1文字分。文字の前後を(')で囲む。
文字列リテラル:複数の文字をデータとして持つリテラルのこと。(")で囲まれたもの。

エスケープシーケンスとは、改行やタブなどを文字リテラルで表すときの文字のことをいう。

主なエスケープシーケンス一覧

\b バックスペース
\f フォームフィード
\n 改行
\r 復帰
\' シングルクオテーション
\" ダブルクオテーション
\\ バックスラッシュ

javaのコードでディレクトリを指定するときに「C:\\temp\\hoge」のように「\」を連続で書くのは、
エスケープシーケンスでバックスラッシュ(\)を表す表記が「\\」であるためである。

■変数について
メンバ変数とローカル変数

変数は記述する場所によってメンバ変数とローカル変数に分けることができる。
メンバ変数は「フィールド」や「プロパティ」と呼ばれる。

class Foo{
	メンバ変数の記述場所
	・インスタンス変数
	・static変数
	
	戻り値の型 メソッド(引数){
	
	}
}

インスタンス変数とは、インスタンスを生成して初めて利用できる変数のこと。
一旦生成されたインスタンス変数はそのオブジェクトが消去されるまで利用することができる。
static変数とは、プログラムの実行を開始した時点ですぐに利用することができる。
つまり、オブジェクトを作成することなく使うことができる。
static変数はメソッド内で宣言することはできない。
つまり、static変数はメンバ変数としてしか宣言できない。

一つのクラスから複数のオブジェクトを生成するとしても、static変数は1つしか生成されない。

class ClassA{
	//メンバ変数
	static int a = 20; //static変数 
	int b = 10;        //インスタンス変数
}

ローカル変数とは、メソッド内で局所的に利用される変数のこと。
メソッド内で利用するので、メソッドローカル変数ともいう。
メソッド内の特定のブロック内で宣言された変数をブロック変数という。
ブロックとは「{}」で囲まれたまとまりのことである。
引数もローカル変数の一種である.

class Foo{
	メンバ変数の記述場所
	・インスタンス変数
	・static変数
	
	戻り値の型 メソッド(引数){
	//ローカル変数の記述場所
	・メソッドローカル変数
	・ブロック変数
	}
}


メソッドローカル変数はメンバ変数と同じ名前にすることも可能だが、同じ名前にしたらメソッドローカル変数が優先される。

メソッド内で同じ名前のメンバ変数を使いたい時はthisキーワードを使う。

public void setX(int x){
	this.x = x;
}

このようにセッターを書くときに「this」と書くのは、メソッドローカル変数xを、同じ名前のメンバ変数xに代入するという意味。
thisを付けないと、xはメソッドローカル変数を指すことになる。

メンバ変数は自動的にデフォルト値に初期化されるが、ローカル変数は自動的に初期化されない。
なので、ローカル変数は明示的に初期化しないとコンパイルエラーが起こる。

メンバ変数のデフォルト値は以下の通り.

boolean false
byte    0
short   0
char    '\u0000'
int		0
long	0L
float	0f
double	0.0D
参照型	null

■equalsメソッド
Objectクラスで定義されているメソッドである。
参照型変数1.equals(参照型変数2)
デフォルトでは参照型変数1に格納されているアドレス情報と参照型変数2に格納されいるアドレス情報が等しいかどうかを判断する。つまり、2つの参照変数が同じインスタンスを参照しているかどうかを判断する。


ちゃんと勉強したい人は以下の書籍を参照してください。

徹底攻略 Java2 プログラマ教科書 Platform 5.0対応 (ITプロ/ITエンジニアのための徹底攻略)
須澤秀人/後藤裕乃
インプレスジャパン
売り上げランキング: 177788

eclipseを使っていると忘れがちなjavaのコマンドライン操作周辺知識のまとめ

javaを実行する際に依存関係のあるクラスは、JVMから検索可能である必要がある。
JVMの検索対象は%JAVA_HOME%/jre/lib/extや標準ライブラリに配置されたモジュールである。
/jre/lib/extディレクトリは拡張ディレクトリと呼ばれ、自動的にCLASSPATHに追加される仕組みである。

具体的には、

C:\Program Files (x86)\Java\jdk1.6.0_31\jre\lib\ext

に配置されたjarファイルならば、JVM(=javaの実行環境)から検索可能であるということである。

それ以外のクラスを利用する場合には、明示的にクラスパスを指定する必要がある。

ちなみにjarファイル内のクラスを利用する場合は、クラス名ではなく、jarファイル名を指定する必要がある。

javacコマンドでクラスファイルの出力先を指定する

tempフォルダ以下にTest.javaがある。

C:\temp\java>dir
2012/07/14  17:28    <DIR>          test
2012/07/14  17:28               100 Test.java

javacコマンドを使ってコンパイルして、classファイルを作成する。
デフォルトではカレントフォルダにclassファイルが作成される。

C:\temp\java>javac Test.java
C:\temp\java>dir
2012/07/14  17:28    <DIR>          test
2012/07/14  17:30               413 Test.class
2012/07/14  17:28               100 Test.java

ディレクトリを指定してclassファイルを出力させたい時
C:\temp\java\classes以下にclassファイルを出力させたい時はオプションに「-d フォルダ名]を指定する。

C:\temp\java>javac -d C:\temp\java\classes Test.java

C:\temp\java\classes>dir
2012/07/14  17:34               413 Test.class

このようにclasses以下にclassファイルが作成された。

注意点
javacの-dで指定するディレクトリは、指定したディレクトリが既に作成済みである必要がある。















javaコマンドの標準オプション
オプション 意味
-cp -classpathオプションの省略形であり、クラスパスを設定するために使用する
-D システムプロパティを設定するために使う



クラスパスは、クラスファイルがある場所を指定します。
例えばC:\temp\java\classesディレクトリ以下にクラスファイルFooがある場合に、
C:\temp\javaディレクトリでjavaコマンドを実行してもエラーが出てしまう。

C:\temp\java>java Foo hello
エラー: メイン・クラスFooが見つからなかったかロードできませんでした

こういうときは、-cpまたは-classpassオプションでクラスパスを指定しなければいけない。

C:\temp\java>java -cp C:\temp\java\classes Foo hello
hello

argsは引数のこと。
args[0]は第一引数を指す。

public class Foo {
	public static void main(String[] args){
		System.out.println(args[0]);
	}
}

このコードを実行するときにHelloと出力するには、引数にhelloを指定する必要がある。

C:\temp\java>java -cp C:\temp\java\classes Foo hello
hello

PHPの入門本が素敵すぎて、それに比べて新人研修が役に立たない理由を考えずにはいられなかった。

「いきなりはじめるPHP」という本を読んだ。




タイトルの通り、いきなり始めていきなり動かすことができる良書である。
この本の最大の魅力は、新人だろうとパソコンにほとんど触れたことがない中年おばさんだろうとほぼ全員例外なくプログラムを動かせることだ。

初めてプログラミングをする人には特にオススメしたい。
プログラミングを習得するには動かしてみるという経験が不可欠であり、本書は「プログラムが動く体験」を確実に提供してくれるからだ。


プログラムを学ぶ上での3つの壁

初心者がプログラミングを習得する過程には3つの壁がある

1つ目は、環境構築の壁である。
2つ目は、プログラムを動かすまでの壁。
3つ目は、美しく動かすまでの壁だ。

今、私がぶつかっているのは3つ目の壁だが、
初めの頃に一番苦労したのは、最初の一歩である環境構築の壁だった。

1つ目の壁は例えるならば、バスケ部に入る以前に親にバッシュを買ってもらえないか、リングがあるのにボールが無いというレベルの問題である。
そんなんじゃシュートが打てないではないか。
ボールがあって初めてシュートができるように、環境を構築して初めてプログラミングの練習ができる。

繰り返しになるが「いきなり はじめる PHP」のいいところは、第一の壁を容易に超える手助けをしてくれて、
プログラムを動かす体験ができるということである。

ブラウザでの表示、画面の切替え、DBの読み込み、書き込みまでひと通り経験できる。
深い説明はないが、わかりやすさを追求するために、あえて深入りを避けているようにも思える。

この本を一冊学ぶことは新人研修3日分の価値がある。
新人研修3日分は、価格にしてなんと15万円である。


新人研修について

私が所属している会社では、入社して2ヶ月半くらい、集中的に研修期間がある。
初めの一ヶ月は社会人としてのマナー研修である。
敬語の使い方、挨拶の仕方、名刺の渡し方からロジカルシンキングまで。
色々とやるが、基本的に内勤のエンジニアでビジネスマナーにうるさい人はあまりいないように思う。

偉い人が自席でクチャクチャ音を立てながら弁当を食っている姿を見るとげんなりするが、
それはビジネスマナーというか人間としてのマナーなので、そっちを大切にしたい。

次に始まるのは技術研修である。

C言語から、(現場で使わない)COBOL、ネットワークの講義まで一週間おきにIT全般の知識を身に付けていく。

研修で学んだことが現場で役に立った、という声はあまり聞いたことはない。
実際今までIT経験がない人が、研修を受けたからといってプログラムが書けるようになりました、なんて人は少ないように思う。

少なくとも(情けないことに)、私が研修でプログラムが書けるようになることはなかった。


新人研修が役に立たない理由

研修で配られるテキストには致命的な欠陥がある。
それは情報量が絶対的に少ないことだ。その情報の少なさには悪意すら感じられる。
「テキスト」としての役割を全く果たしていないのだ。
なぜかというと、パワーポイントをベースにしてテキストを作っているからだ。

箇条書きのテキストは、人間が何かを理解するのに適していない。
講義が前提にあるのかもしれないが、聞いたことを全てメモできるわけではないし、
メモすることは最初から"文章にして"テキストに書いておくべきなのである。
情報量が少なすぎて、リファレンスとしても使えない。
新人研修のテキストを研修が終わってから参照することは一度もなかった。

新人研修が役に立たないもう一つの理由は、動かすプログラムの絶対量が少ないことが問題だ。
ひたすら眠くなるテキストを読み上げるだけの講義を聞いて、そのあとやっと演習に入る。
そしてプログラムを完成させてみて、講師にチェックしてもらうのが研修の常だ。

講義が無駄なのだ。
講師が講義していることなんてググればわかるし、そもそもテキストを読み上げるくらいなら最初から書けばいい。
講師が語るべきは現場での体験であり、経験値であり、ググればわかることではない。
「なぜそうなるのか」を説明しない講義はそもそも論外である。

テキストを読み上げる眠い講義をするよりも、
テキストに全てを書いて、ググりながら課題を解決させて、講師が寝て待っていたほうがよっぽど効果的だ。
わからないところだけ聞かれたら教えればいいのである。

箇条書きのテキストを使って眠くなる講義をして、その研修にかかる費用はなんと

一日5万以上

である。

ちなみにこれは一人あたりの費用だ。
なんと一日の講義でオライリーの高い本でも10冊は買える計算である。

管理職も、部下を研修に行かせるくらいなら、一冊本を与えて、業務時間にそれをやる時間を与えればいいのだ。

上から変なタスクを振られない時間があるという点で研修は有用だが、知識を集中的に身につけるなら絶対に本を読んだほうがいい。

「この研修に行ったから技術が身に付きました」なんていうのもナンセンスだし、研修に行けば技術が身に付くと勘違いしている人は早々に考えを改めたほうがいい。

というか研修でPHP入門するくらいなら、「いきなりはじめるPHP」を読んだ方が絶対いい。

Springのセッターインジェクションを使ってみる~JavaでFTPを実行するツールの改修~

昨日Springをインストールしたので、
http://gomyownway.hatenablog.com/entry/2012/07/07/214727

せっかくだからFTPを実行するコードにセッターインジェクションを組み込んでみた。

FTPを実行するクラス

package ftptool;

import java.io.FileInputStream;
import java.io.IOException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

public class SendFileByFtp {
	private static int port;
	private static String host;
	private static String user;
	private static String passWord;
	private static int fileType;
	private static String sendLocalFileName;
	private static String sendRemoteFileName;
	private static String makeRemoteDirectoryName;
	
	public void setPort(int port){
		this.port = port;
	}
	public void setHost(String host){
		this.host = host;
	}
	public void setUser(String user){
		this.user = user;
	}
	public void setPassWord(String passWord){
		this.passWord = passWord;
	}
	public void setSendLocalFileName(String sendLocalFileName){
		this.sendLocalFileName = sendLocalFileName;
	}
	public void setFileType(int fileType){
		this.fileType = fileType; 
	}
	
	public void setSendRemoteFileName(String sendRemoteFileName){
		this.sendRemoteFileName = sendRemoteFileName;
	}
	public void setMakeRemoteDirectoryName(String makeRemoteDirectoryName){
		this.makeRemoteDirectoryName = makeRemoteDirectoryName;;
	}
	
	public static void sendFile(){
		boolean error = false;
		//FTPクライアントをDIする書き方わからん
		FTPClient ftpClient = new FTPClient();
		FileInputStream fis = null;
		try{
			ftpClient.connect(host,port);
			System.out.println(host+" に接続しています...");
			System.out.println(ftpClient.getReplyString());
			
			int reply = ftpClient.getReplyCode();
			
			if(!FTPReply.isPositiveCompletion(reply)) {
				ftpClient.disconnect();
				System.err.println("FTP接続を拒否されました");
				System.exit(1);
			}
			
			if(ftpClient.login(user, passWord) == false){
				System.out.println("ログインに失敗しました");
				System.exit(2);
			}
			
			//ファイルの転送を行う
			fis = new FileInputStream(sendLocalFileName);
			ftpClient.setFileType(fileType);
			ftpClient.makeDirectory(makeRemoteDirectoryName);
			ftpClient.storeFile(makeRemoteDirectoryName+sendRemoteFileName,fis);
			
			System.out.println("FTP接続を切ります");
			ftpClient.logout();
			
		}catch(IOException e){
			error = true;
			e.printStackTrace();
		} finally {
			if(ftpClient.isConnected()) {
				try{
					ftpClient.disconnect();
				}catch(IOException ioe){
					
				}
			}
			System.exit(error ? 1 :0);
		}
		
	}
	
	
}

特筆すべきは、プロパティは全てBeans.xmlに書いてあるから、宛先を変えるときにプログラムを修正する必要がないということ。
こうすることによって、モジュールごとのつながりが疎結合になって、変更に強いプログラムになった。

beans.xmlはこんな感じ。

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
    <bean id="sendFileByFtp" class="ftptool.SendFileByFtp" >
        <property name="port">
            <value>21</value>
        </property>
        
        <property name="host">
            <value>sakura</value>
        </property>
        
        <property name="user">
            <value>XXXXXX</value>
        </property>
        
        <property name="passWord">
            <value>XXXXXXXXXX</value>
        </property>
        
        <property name="sendLocalFileName">
            <value>C://Users//hoge//Desktop//tmp2//hoge.txt</value>
        </property>

        <property name="fileType">
            <value>2</value>
        </property>
        
        <property name="sendRemoteFileName">
            <value>foo.txt</value>
        </property>

        <property name="makeRemoteDirectoryName">
            <value>/var/www/ftptest/</value>
        </property>
    </bean>
    
    
</beans>

このbeans.xmlに設定を書けば、あとはセッターを通じてSpringが値を注入してくれる。
これをDI(Dependency Injection)という。

最後、mainクラスはこんな感じ。

package ftptool;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Main {
	public static void main(String[] args){
		
		BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
		SendFileByFtp sendFileFtp = factory.getBean("sendFileByFtp",SendFileByFtp.class);
		sendFileFtp.sendFile();
	}
}

結果として、ローカルのhoge.txtがFTPサーバの

/var/www/ftptestディレクトリ以下に

foo.txt

という名前で転送されます。


ちなみに前に書いたJavaFTPするコードはこんな感じです。
http://gomyownway.hatenablog.com/entry/2012/06/24/234212

前よりずいぶんすっきりしましたよねw
プロパティをハードコーディングするのは絶対良くないので。

あ、あと、
twitterやってます→@go_my_own_way
https://twitter.com/go_my_own_way

気軽にフォローしてください~(フォロワーいなくて寂しいw)

全くの初心者がSpringフレームワークをダウンロードしてからeclipseで動かすまで

Springを公式サイトからダウンロードしてからeclipseで軽く動かすまでの手順を載せます。
僕は半年前までフレームワークを使ったことがなく、eclipseフレームワークを使うこともなかったのですが、全くの初心者である自分に向けるつもりでまとめます。


①Springsource communityにページにいく
http://www.springsource.org/


②右下にある「Spring Framework」をクリックする
f:id:go_my_own_way:20120707211650p:plain


③「Spring Framework 3.0.7.RELEASE」の「Download」をクリックする
f:id:go_my_own_way:20120707211733p:plain


④メールアドレス等を登録する。
登録してもメールが来たりはしません。

⑤More >>「spring-framework-3.0.2.RELEASE-with-docs.zip」をクリック
f:id:go_my_own_way:20120707211817p:plain

eclipseのプロジェクトを右クぷリック→プロパティ→Javaのビルドパス
f:id:go_my_own_way:20120707211914p:plain

ダウンロードしたzipを展開したらできる、
\spring-framework-3.0.2.RELEASE-with-docs\spring-framework-3.0.2.RELEASE\dist以下のJarを追加しておく。

・commons-logging.jarも外部jarに追加しないとエラーが出る
http://sourceforge.jp/projects/sfnet_webolab/downloads/0.9/lib/commons-logging.jar/


ここからは、サンプルを作ってみます。
プロジェクト配下に「sample1」というパッケージを作ってください。

sample1の下に、以下の3つのファイルを作成します。


Main.java

package sample1;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class Main {
	public static void main(String[] args){
		BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
		CallName cn = factory.getBean("callName",CallName.class);
		cn.callName("go my own way!");
	}
}


CallName.java

package sample1;

public interface CallName {
	public void callName(String name);
}


CallNameImpl.java

package sample1;

public class CallNameImpl implements CallName{
	public void callName(String name){
		System.out.println("good morning!" + name);
	}
}


で、プロジェクト直下にbeans.xmlを配置します。

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="callName" class="sample1.CallNameImpl" />
</beans>


eclipseでフォルダ構成を見るとこんな感じ。
f:id:go_my_own_way:20120707213316p:plain


実行結果
f:id:go_my_own_way:20120707213324p:plain

BeanFactoryに定義されているメソッドで、
getBean(id名, Class名)
があるけれど、ここはbeans.xml

<bean id="callName" class="sample1.CallNameImpl" />

ここの部分の記述に対応しています。
getBeanは引数に指定された名前のBeanインスタンスを生成して返します。

<bean ~~~ />

このまとまりを「bean」といって、

idとは、Beanに対して付ける識別子です。これは一意でなければいけません。
classとは、Beanのクラス名のことです。ここは完全修飾名で記述します。

注目すべきは、Mainでnewを使ったインスタンス化をしていないことです。

インスタンス化はSpringがやってくれて、オブジェクトを渡してくれるのです。

ここまで参考にした書籍は下記です。

次は、この前書いたFTPの転送ツールをセッターインジェクションを使って書き直してみたいと思います。

javaでCSVファイルを読み込み、形を変えてCSVで出力するプログラムを作った

新人の時、「夜間に動くバッチで、CSV形式のファイルを読み込んで、ちょっと形を変えてCSVにして出力するJavaプログラムを作成せよ」的な司令が下った。
あの時はあの時で色々苦労したような気がする。

具体的にどんなだったか全然覚えてないんだけれど、とりあえずCSVtoCSV的な、ファイルを読み込んでちょっと整形してまた出力するJavaプログラムを作ってみた。

ちなみにCSVとは、Comma Separated Valuesの略で、カンマで区切ったデータを並べたファイルである。

■input.csv
以下のようなinput.csvを作った。

maeda,oshima,sashihara,shinoda
kojima,takahashi,kuramochi
maebara,maeda,nakata,Takajo

このCSVファイルを読んでいって、「sashihara」という文字列があったら、
これを「sashiko」に変換してoutput.csvに出力するプログラムを書いてみる。

■CSV2CSVクラス
上にあるinput.csvを読み込んで、変換してまたcsvにするプログラム。
ちょっといじればタブ区切りとか、スペース区切りにして出力もできます。

package csv2csv;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CSV2CSV {
	private final static String inputCsvFile = "C:\\temp\\input.csv";
	private final static String outputCsvFile = "C:\\temp\\output.csv";
	private final static String ChangeName = "sashihara";
	private final static String ChangedName = "sashiko";
	
	public static void main(String[] args){
		String line;
		String[] csvArray;
		File inputFile = new File(inputCsvFile);
		File outputFile = new File(outputCsvFile);
		BufferedReader in = null;
		BufferedWriter out = null;
		try {
			in = new BufferedReader(new FileReader(inputFile));
			out = new BufferedWriter(new FileWriter(outputFile));
			
			/** 1行ずつ読み込んで整形する */
			while ((line = in.readLine()) != null){
				
				//区切り文字カンマ[,]で、区切られた文字列を配列に格納する
				csvArray = line.split("\\,");
				
				//配列の長さ分、データを出力していく
				for(int i=0; i < csvArray.length; i++){
					
					//行の最後のデータはカンマ付けない
					if(i != csvArray.length -1 ){
						if(ChangeName.equals(csvArray[i])){
							//[sashihara]は[sashiko]に変換して出力する
							csvArray[i] = ChangedName;
							out.write(csvArray[i]);
							out.write(",");
						}else{
							out.write(csvArray[i]);
							out.write(",");
						}
					} else{
						out.write(csvArray[i]);
					}
				}
				//newLineは改行する合図
				out.newLine();
			}
			
		}catch(FileNotFoundException e){
			e.printStackTrace();
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			//fanallyは絶対実行される。とりあえずファイルは閉じるように。
			try{
				if (in != null){
					in.close();
				}
				if(out != null){
					out.close();
				}
			}catch(IOException e){
				System.out.println("close fail");
				e.printStackTrace();
			}
		}
		
	}
	
}

■output.csv
「sashihara」を「sashiko」に変換して出力した結果。

maeda,oshima,sashiko,shinoda
kojima,takahashi,kuramochi
maebara,maeda,nakata,Takajo

このように、指原がさしこになって出力されています。

いま書くと30分くらいで書ける処理も新人のときは泣きそうになりながらgoogleで答えを探して、でもなんだかよくわからなくて、路頭に迷ってた気がする(笑)

以下、新人の時の自分へ。

君はいつもググってばかりいるけど、一向に仕事が進まないね。
googleはたしかに便利だ。
どんな情報も手に入るかのように錯覚してしまいそうだ。

でも、googleは断片的な情報を与えてくれるけど、体系的な知識はなかなか与えてくれない。

体系的に学んでいないことは理解できないし、いま君が陥っているように答えを探すにもすごく時間がかかってしまう。

逆に、一回体系的に学べば、だいたいのことは調べながらできるし、検索の速度もめちゃくちゃ速くなる。

googleはあくまで補助に使うもので、基礎となるのは普段の勉強なんだ。

君は家でよく勉強しているけど、コードは読むだけじゃ身につかない。
書いて動かそう。君がバスケでずっと基礎練習を繰り返してきたように、繰り返し繰り返し反復して技術を磨くしかないんだ。

君は「10日でわかる~」とか「かんたん~」とか、「はじめての~」とかそんな本ばかり買っているけど、簡単そうに見える本に限って中身が薄くて実はあまり理解できないものなんだ。
最初は薄くてわかりやすそうな本を使うのは受験では鉄則だったけど、プログラミングでは少し厚くて詳しく書いてある本を使おう。

君の検討を祈る。
P.S.
試験なんてクソ食らえと思ってたけど、Javaの基礎勉強にはこれがすごく役に立ったよ。読んでみるといい。

JavaでファイルをFTPするツール

いちいちFFFTPを起動してファイルを手作業で転送するのが面倒なので、バッチに組み込んで自動でFTPするツールを作ろうと思い立った。

引数に送信テキスト名を渡してキックすれば、手作業を省略できるのではないか、と(何言ってるのか意味不明かと思いますが、私事ですいません)

とりあえず実装はディレクトリ構成を見直さないとちゃんとできないので、今日はJavaFTPするコードのサンプルだけ作ってみた。


まず、JavaFTPするには準備が必要となる。

①JakartaのCommons Netをダウンロードしよう
http://commons.apache.org/net/download_net.cgi

「commons-net-3.1-bin.zip」
というzipファイルをダウンロードしてくればOK。

javaFTPを実行する際のAPIが提供されている。

Eclipseのビルドパスを通す。
・プロジェクト名を右クリック→プロパティ
Javaのビルドパス→外部Jarの追加
・①でダウンロードしたzipを解凍して、中にあるJarを全て選択する

③サンプルコードはこんな感じ

import java.io.FileInputStream;
import java.io.IOException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

public class FTPFile {
	private static int PORT = 21;
	private static String HOST = "ホスト名を記入";
	private static String USER = "ユーザ名を記入";
	private static String PASSWORD = "パスワードを記入";
	
	
	public static void main(String[] args) {
		String SEND_FILE_NAME;
		
                //引数から送信ファイル名を取得
		SEND_FILE_NAME = args[0];
		FTPClient ftpClient = new FTPClient();
		FileInputStream fis = null;
		try {
			
			ftpClient.connect(HOST,PORT);
			System.out.println(HOST+"に接続しています...");
			System.out.println(ftpClient.getReplyString());
			
			int reply = ftpClient.getReplyCode();
			
			if(!FTPReply.isPositiveCompletion(reply)) {
				ftpClient.disconnect();
				System.out.println(HOST+"に接続を拒否されました");
				System.exit(1);
			}
			
			if (ftpClient.login(USER,PASSWORD) == false) {
				System.out.println("ログインに失敗しました");
				System.exit(2);
			}
			
			fis = new FileInputStream(SEND_FILE_NAME);
			//バイナリモードで送信
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			//ファイルを送信
			ftpClient.storeFile(SEND_FILE_NAME,fis);
			//ディレクトリを作成するとき
			ftpClient.makeDirectory("/hoge/");
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(ftpClient.isConnected()){
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					// TODO 自動生成された catch ブロック
					e.printStackTrace();
				}
			}
			
			if (fis != null) {
				try {
					fis.close();
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

このサンプルをちょっと応用して、送信テキストを引数にしてJavaを実効するバッチを作る。
そして、引数を渡して、バッチをキックするVBAを作れば、面倒なFTPの作業は自動化できるはず!

SQL作成が面倒な人なための、コピペするだけで使えるUPDATE文作成自動化VBA入門

Excelでデータを編集していて、このセルで編集したデータをそのままSQLにできたらいいのに、と思う人はきっといるはず。

そんな人のために、前回のINSERT文生成自動化マクロに引き続き、UPDATE文を生成するマクロを作ってみた。

コピペするだけで使えるので、そのままコピペするなり,改変するなりして、使ってみてください。

BentoBox(TM) : みんなのアプリを、みんながつくってシェアする

①プロパティを設定する。
f:id:go_my_own_way:20120624202350p:plain

②データはこのように「●」「★」で区切って格納する。何行でも入れることができる。
f:id:go_my_own_way:20120624202507p:plain

VBAのコードはこんな感じ

Const Properties As String = "プロパティ"
Const UPDATE_SHEET As String = "UPDATE"
Option Explicit

Sub UPDATE文生成()
    Dim TABLE_NAME As String
    Dim Column As Long
    Dim Row As Long
    Dim END_Column As Long
    Dim START_WHERE_Column As Long
    Dim END_WHERE_Column As Long
    Dim OutputFolder As String
    Dim OutputTextName As String
    Dim OutputText As String
    Dim reply As Integer

    
    '--------------------
    ' プロパティの設定
    '--------------------
    TABLE_NAME = Worksheets(Properties).Range("B2").Value
    OutputFolder = Worksheets(Properties).Range("B3").Value
    OutputTextName = Worksheets(Properties).Range("B4").Value
    
    
    '---------------------------------
    'フォルダの末尾が[\]でなければ追記
    '---------------------------------
    If Right(OutputFolder, 1) <> "\" Then
        OutputFolder = OutputFolder & "\"
    End If
    
    OutputText = OutputFolder & OutputTextName
    
    reply = MsgBox(OutputFolder & " 以下にUPDATE文を生成します。よろしいですか?", vbYesNo)
    
    If reply = vbNo Then
        Exit Sub
    End If
    
    '--------------------
    ' カラムの末尾の設定
    '--------------------
    Column = 2
    Do While Worksheets(UPDATE_SHEET).Cells(2, Column) <> "●"
        Column = Column + 1
    Loop
    'カラム末尾の列番号
    END_Column = Column - 1
    START_WHERE_Column = Column + 1
    
    '--------------------------------
    ' WHERE条件の末尾の列番号を取得
    '--------------------------------
    Do While Worksheets(UPDATE_SHEET).Cells(2, Column) <> "★"
        Column = Column + 1
    Loop
    
    END_WHERE_Column = Column - 1

    Row = 4
    Open OutputText For Output As #1
    Do While Cells(Row, 1).Value <> "●"
        
        Print #1, "UPDATE " & TABLE_NAME
        Print #1, "SET"

        For Column = 2 To END_Column
            
                '--------------------------------
                ' 最終カラム以外は[,]で区切る
                '--------------------------------
                If Column <> END_Column Then
                    
                    '------------------------------
                    ' 空白の場合はNULL
                    '------------------------------
                    If Cells(Row, Column).Value = "" Then
                        Print #1, Cells(2, Column) & "= NULL,"
                    Else
                        '-----------------------------
                        ' シングルクォテーションの要否
                        '-----------------------------
                        If Cells(3, Column).Value = "○" Then
                            Print #1, Cells(2, Column) & " = '" & Cells(Row, Column).Value & "',"
                        Else
                            Print #1, Cells(2, Column) & " = " & Cells(Row, Column).Value & ","
                        End If
                    End If
                    
                Else
                    
                    '----------------------------
                    ' 空白の場合はNULL
                    '----------------------------
                    If Cells(Row, Column).Value = "" Then
                        Print #1, Cells(2, Column) & "= NULL"
                    Else
                        '-----------------------------
                        ' シングルクォテーションの要否
                        '-----------------------------
                        If Cells(3, Column).Value = "○" Then
                            Print #1, Cells(2, Column) & " = '" & Cells(Row, Column).Value & "'"
                        Else
                            Print #1, Cells(2, Column) & " = " & Cells(Row, Column).Value
                        End If
                    End If
                End If
        
        Next
        
        '--------------------------------
        ' WHERE文以下を記載
        '--------------------------------
        Print #1, "WHERE"
        For Column = START_WHERE_Column To END_WHERE_Column
            
            '-------------------------------------------------
            'WHERE文の最終列かどうか。最終列以外なら[AND]が必要
            '------------------------------------------------
            If Column <> END_WHERE_Column Then
                '----------------------------
                ' 空白の場合はNULL
                '----------------------------
                If Cells(Row, Column).Value = "" Then
                    Print #1, Cells(2, Column) & "= NULL AND"
                Else
                    Print #1, Cells(2, Column) & " = " & Cells(Row, Column) & " AND"
                End If
            
            Else
                '----------------------------
                ' 空白の場合はNULL
                '----------------------------
                If Cells(Row, Column).Value = "" Then
                    Print #1, Cells(2, Column) & "= NULL"
                Else
                    Print #1, Cells(2, Column) & " = " & Cells(Row, Column)
                End If
            
            End If
        Next
        
        
        Print #1, ";"
        Row = Row + 1
    Loop
        
    Close #1

    MsgBox "UPDATE文を生成しました"

End Sub

④上記のマクロを実行すると、設定したフォルダにSQLが出力される。
f:id:go_my_own_way:20120624202810p:plain

⑤テキストの中身はこんな感じ

UPDATE emp_copy
SET
ename = '林',
deptno = 20
WHERE
empno = 1009 AND
deptno = 20
;
UPDATE emp_copy
SET
ename = '小林',
deptno = 30
WHERE
empno = 1010 AND
deptno = 30
;
UPDATE emp_copy
SET
ename = '中林',
deptno = 40
WHERE
empno = 1011 AND
deptno = 20
;
UPDATE emp_copy
SET
ename = '大林',
deptno = 50
WHERE
empno = 1012 AND
deptno = 30
;

SQLを実行した結果
f:id:go_my_own_way:20120624202952p:plain


以下、余談。
このページを見てくれた誰かが、必要なコードを書く時の参考にしてもらえたら嬉しいと思って記事を書いた。
例えば、VBAなんて書いたこともない新人のために。

しかし、自分がプログラムなんてクソも書けなかった新人時代を振り返ると、自分が最も欲しかった情報は

「どうやったらプログラムが書けるようになるの?」

ということだった。

そして、ソースをコピペするだけでは絶対にプログラムを書けるようにはならないのである。

プログラムを書くために必要なことは、第一に

「自分がコンピューターに何をやらせたいのか」

を明確に思い描くことである。

次に、

「やらせたいことを実現するためには、コンピューターにどういう手順を踏ませればよいか」

を組み立てること。

最後に、組み立てた手順をコードにすることである。

そして、手順を考えるために必要な練習は、普段から手を動かして、サンプルを作ったり、ソースを読むことだ。

無から有は生まれないように、まずは実際にサンプルを作って処理を実行してみたり、ソースを読んで処理を追ってみた"経験"があり、
それらを組み合わせることで、手順(いわゆるロジック)を組み立てることができる。

ことVBAに限っては、プログラミングの練習として非常にわかりやすいので、自宅で適当にサンプルを作って動かすといいと思う。
SIerで働く限り、幸か不幸かExcelを使った単純作業を避ける事などできないのだから。

INSERT文の作成をVBAで自動化する

データパッチを当てるとき、Excelでデータを整理し、さてパッチ当てのSQLを作ろうとしたとき、あまりの量の多さにめまいがしたことはないだろうか。

このExcelの値をそのままSQL文にできたらいいのに、と誰もが思うはずである。

単純作業はコンピュータにやらせて、人間は頭を使ったほうがいい。
というわけで、SQLを生成するマクロを作ってみた。
まずは作成ロジックが簡単なINSERT文から。
BentoBox(TM) : みんなのアプリを、みんながつくってシェアする

以下は、INSERT文の作成を自動化するVBAのサンプルである。
コメントをつけたので、必要に応じて書き換えて使ってください。

①プロパティを設定する
f:id:go_my_own_way:20120623152032p:plain

②データのテーブルを記載するフォーマット
f:id:go_my_own_way:20120623152211p:plain
※列や行の末尾は「●」で判別するようにした。

③ ②で設定したフォーマットに入れた値をINSERT文にして吐き出すVBAサンプル。

Const Properties As String = "プロパティ"
Const INSERT_SHEET As String = "INSERT"
Option Explicit

Sub INSERT文生成()
    Dim TABLE_NAME As String
    Dim Column As Long
    Dim Row As Long
    Dim END_Column As Long
    Dim OutputFolder As String
    Dim OutputTextName As String
    Dim OutputText As String
    
    
    '--------------------
    ' プロパティの設定
    '--------------------
    TABLE_NAME = Worksheets(Properties).Range("B2").Value
    OutputFolder = Worksheets(Properties).Range("B3").Value
    OutputTextName = Worksheets(Properties).Range("B4").Value
    
    '---------------------------------
    'フォルダの末尾が[\]でなければ追記
    '---------------------------------
    If Right(OutputFolder, 1) <> "\" Then
        OutputFolder = OutputFolder & "\"
    End If
    
    OutputText = OutputFolder & OutputTextName
    
    '--------------------
    ' カラムの末尾の設定
    '--------------------
    Column = 2
    Do While Worksheets(INSERT_SHEET).Cells(2, Column) <> "●"
        Column = Column + 1
    Loop
    'カラム末尾の列番号
    END_Column = Column - 1

    Row = 4
    Open OutputText For Output As #1
    Do While Cells(Row, 1).Value <> "●"
        Column = 2
        
        Print #1, "INSERT INTO " & TABLE_NAME & "("
        For Column = 2 To END_Column
            
            '-------------------------------
            ' カラム名を出力する
            ' 最終カラム以外は[,]で区切る
            '--------------------------------
            If Column <> END_Column Then
                Print #1, "   " & Cells(2, Column).Value & ","
            Else
                Print #1, "   " & Cells(2, Column).Value
            End If
        Next
        
        Print #1, ")"
        Print #1, "VALUES("
        For Column = 2 To END_Column
            
                '--------------------------------
                ' 最終カラム以外は[,]で区切る
                '--------------------------------
                If Column <> END_Column Then
                    
                    '------------------------------
                    ' 空白の場合はNULL
                    '------------------------------
                    If Cells(Row, Column).Value = "" Then
                        Print #1, "   NULL,"
                    Else
                        '-----------------------------
                        ' シングルクォテーションの要否
                        '-----------------------------
                        If Cells(3, Column).Value = "○" Then
                            Print #1, "   '" & Cells(Row, Column).Value & "',"
                        Else
                            Print #1, "   " & Cells(Row, Column).Value & ","
                        End If
                    End If
                    
                Else
                    
                    '----------------------------
                    ' 空白の場合はNULL
                    '----------------------------
                    If Cells(Row, Column).Value = "" Then
                        Print #1, "   NULL"
                    Else
                        '-----------------------------
                        ' シングルクォテーションの要否
                        '-----------------------------
                        If Cells(3, Column).Value = "○" Then
                            Print #1, "   '" & Cells(Row, Column).Value & "'"
                        Else
                            Print #1, "   " & Cells(Row, Column).Value
                        End If
                    End If
                End If
        
        Next
        Print #1, ");"
        Row = Row + 1
    Loop
        
    Close #1

End Sub

上記のマクロを実行した結果は以下の通り。

INSERT INTO dept_copy(
   DNAME,
   DEPTNO,
   LOC
)
VALUES(
   'システム',
   70,
   '横浜'
);
INSERT INTO dept_copy(
   DNAME,
   DEPTNO,
   LOC
)
VALUES(
   '開発',
   80,
   '東京'
);
INSERT INTO dept_copy(
   DNAME,
   DEPTNO,
   LOC
)
VALUES(
   '経理',
   90,
   NULL
);
INSERT INTO dept_copy(
   DNAME,
   DEPTNO,
   LOC
)
VALUES(
   'プログラム',
   NULL,
   '福岡'
);

作成したSQLをSQLplusで実行した結果。
f:id:go_my_own_way:20120623152800p:plain

実際の現場では、SQL DeveloperやObject Browzerを使って、作成したSQLをまとめて実行するといいと思います。

サンプルを書くのにだいたい1時間くらいかかりますが、このブログを見てくれた人は上のコードをコピペして使えば5分で済むし(笑)、
手作業でSQLを書くよりも全然効率がよくなるはず!

次はUPDATE文を自動生成するVBAを作ります~。

単純作業に屈してはならない。エンジニアでいたいから。

初めて部署に配属されたときにオフィスの中を見ていて、素朴な疑問を抱いた。

「なんでこの人達はこんなに電話ばっかりしてるんだろう」
「なんでこの人達はこんなに会議ばっかりしてるんだろう」
「なんでこの人達は会議で何も発言せずにパソコンばっかり見てるんだろう」

配属されてから4ヶ月が経ち、新たな疑問を抱いた。

「なんでアルバイトがやるような単純作業をエンジニアがやるんだろう」
「自分がやってる『作業』に意味はあるんだろうか」

SIerに勤務している社員が電話ばかりしているのは、協力会社との調整があるからだ。
協力会社に指示を出し、進捗を管理しなければならない。
そして、その進捗をさらに上の上司に報告する。
SIerの社員の日中はほとんどが会議と報告、そのための資料作りに費やされる。

そして、必要と思えない会議まで定例化され、資料に書いてあることを読み上げるだけの会議が日にいくつも設定される。

これはSIerの仕組みであり、権力を持つ人間が仕組みを作る。
年功序列がまかり通る大企業で、若手がどうこう言ってもなかなか変わらない仕組みでもある。仕組みを変えるには力をつけなれけばならない。
でも、それには時間がかかる。ならば力がつくまでじいっと我慢せねばならない。

しかし、エンジニアなら。
単純作業を許容してはいけない。

「これがSIの仕事だから」

などといって、Excelの単純集計を延々とやって時間を無駄にしてはいけないのだ。
単純な繰り返しは『作業』だが、作業を自動化する仕組みを作るのは『仕事』である。
技術を身に付けて、仕事をしよう。

例えば。
僕は社会人1年目10月に、こんな指示が与えられた。

「1,000ファイルあるexcelを一つ一つ開いて、正しいフォーマットで記載されているかチェックしてくれ」

いま思い出すと、これはエンジニアの仕事だとは思えない。
大学生のアルバイトの仕事である。
ひたすら、一日中やっていた作業は

・Excelファイルを開いて、所定の位置に文字列がちゃんと記載されているか。
・Excelファイルを開いて、条件付き書式が設定されているか。

の確認だった。

繰り返しになるが、
こんな単純作業は、エンジニアがやる仕事ではない。
アルバイトがやる仕事ですらない。
コンピュータがやる作業である。

今こそ俺は、新人時代の情けない自分にリベンジしたい。

まず、かつての調査資料のサンプルを作成した。
f:id:go_my_own_way:20120616165214p:plain
これは、2行目から4行目まで条件付き書式が設定してある。
セルが空白だった場合、セルを赤くする条件だ。

条件付き書式は以下のように設定した。
f:id:go_my_own_way:20120616165423p:plain
「ホーム」の「条件付き書式」の「新しいルール」を選ぶ。

f:id:go_my_own_way:20120616165528p:plain
次に「数式を使用して、書式設定するセルを決定」で

=isblank(D2)

と入力する。

セルの右下の十字のところをビョーンとD4セルまで伸ばすと、
f:id:go_my_own_way:20120616165736p:plain
このようにセルごとに条件が設定される。

こうやって作った調査資料を4つ、フォルダにまとめた。
f:id:go_my_own_way:20120616165931p:plain


新人のときは本当に何もできなかった。
こんな単純作業に疑問を抱きながらも、泣きそうな顔をしながら、一つ一つのファイルを開いた。
罰ゲームでもこなすかのように。

でも今なら泣かない。VBAがあるから。

'定数はSubプロシージャの外で定義しておく
Const 調査結果シート As String = "調査結果"
Const 調査シート As String = "Sheet1"


Sub シートの条件チェック()
    Dim 結果 As String
    Dim 調査シート行 As Long
    Dim 対象シート行 As Long
    Dim ファイル名一覧 As Variant
    Dim 調査対象ブック As Workbook
    Dim 対象行 As Long
    
    'ファイル名を配列にして取得する
    ファイル名一覧 = Application.GetOpenFilename("調査対象シート,*.xlsx", MultiSelect:=True)
    
    'ファイルが選択されなかったらSubプロシージャ終了
    If VarType(ファイル名一覧) = vbBoolean Then Exit Sub
    
    'UBoundは配列の大きさを取得する関数。選ばれたファイルの数だけ順に開く
    For i = 1 To UBound(ファイル名一覧)
        
        '調査対象の開始行
        Set 調査対象ブック = Workbooks.Open(ファイル名一覧(i))
        With ThisWorkbook.Worksheets(調査結果シート)
            '調査対象ブック名を書き込む
            .Cells(i + 1, 1) = 調査対象ブック.Name
            
            結果 = "無"
            '対象の列に空白がないかチェックする
            For 対象行 = 2 To 4
                '空白がある場合「有」を設定
                If 調査対象ブック.Worksheets(調査シート).Range("D" & 対象行).Value = "" Then
                    結果 = "有"
                End If
            Next
            .Cells(i + 1, 3) = 結果
            
            '調査対象列のセルに条件付き書式が設定されいているか調べる
            結果 = "○"
            For 対象行 = 2 To 4
                '条件付き書式が設定されていない場合に出るエラーを無視する
                On Error Resume Next
                
                '条件付き書式が設定されていない場合「×」をつける
                If 調査対象ブック.Worksheets(調査シート).Range("D" & 対象行).FormatConditions(1).Formula1 = "" Then
                    結果 = "×"
                End If
            Next
            .Cells(i + 1, 2) = 結果

        End With
        '調査対象のブックを保存しないで閉じる
        調査対象ブック.Close SaveChanges:=False
    
    Next
    
End Sub

上のVBAを実行すると、
f:id:go_my_own_way:20120616170518p:plain
このような結果が出力される。

これは想定通りで、
4つあった資料はそれぞれ以下のようになっていた。
調査資料1:
・条件付き書式は全てに設定
・空白なし
f:id:go_my_own_way:20120616170613p:plain

調査資料2:
・条件付き書式は全てに設定
・空白あり
f:id:go_my_own_way:20120616170704p:plain

調査資料3:
・条件付き書式一部無し
・空白あり
f:id:go_my_own_way:20120616170736p:plain

調査資料4:
・条件付き書式一部無し
・空白なし
f:id:go_my_own_way:20120616170918p:plain


で、再度掲載。結果はこの通り。
f:id:go_my_own_way:20120616170935p:plain

業務でやる場合、何行目におかしな部分があるかも出力するべきだけど、
書いているうちに気が済んできた(笑)

だが、課せられた単純作業はこれだけではない。
ときどき思い出すたびにかつての自分に復讐したいと思う。

そう。
恨むべくは単純作業を課したボスではなく、単純作業をコンピュータにやらせる技術がなかった自分なのだ。

無駄に気付くたびに単純作業は自動化し、チームに貢献したいと思っている。

そして、VBAを勉強するのにものっすごく役に立ったのが
「やさしくわかる Excel VBA
だ。この一冊があれば、たいていの単純作業は自動化できると思うし、わからないところをGoogleで検索するときも、ものすごく効率が良くなると思う。

コマンドプロンプトを立ちあげ障害を追っていく先輩に憧れて

PCを使う仕事をしている限り、ネットワークの問題は切り離せない。
それは個人の問題であれ、システムの問題であれ、常日頃何かが起こり、そして原因を追跡しなれけばならない。
スリーウェイハンドシェイクという言葉すら知らなかった新人時代、問題が起きたら即時にteratermを立ちあげ、問題点を追跡していく先輩はさながら探偵のようにも見えた。
意味がわからないものは神々しく見えるものなのだ。そんなに難しいことをしていたわけではないのに。
以下、コマンドプロンプトでネットワーク系のコマンドを思うがままにまとめていく。
詳しく学びたい人は、文末に載せたリンクの書籍を読んでほしい。

■例えば迷惑メールが届いて、その迷惑メールの送り手が使っているISPまでをなんとか調べたいとき。
①nslookupを使って、メールを送ってきたメールサーバを調べる。

C:\temp>nslookup
DNS request timed out.
    timeout was 2 seconds.
既定のサーバー:  UnKnown
Address:  192.168.1.1
> set type=MX
>
> pinky-pin.XX ←迷惑メールの@以下
サーバー:  UnKnown
Address:  192.168.1.1

権限のない回答:
pinky-pin.jp    MX preference = 10, mail exchanger = smtp.pinky-pin.jp

pinky-pin.jp    nameserver = ns5.vnstg.net
pinky-pin.jp    nameserver = ns2.vnstg.net
pinky-pin.jp    nameserver = ns4.vnstg.net
smtp.pinky-XXX.jp       internet address = 202.251.XXX.XX(ここにIPアドレスが表示される)

②whois sourceというwebサイトに上記で取得したIPアドレスを入力すると、このIPアドレスを割り振っているISPが表示される。
http://www.whois.sc/

DNSキャッシュを確認する

C:\temp>ipconfig /displaydns

Windows IP 構成

    vivi.dyndns.org
    ----------------------------------------
    レコード名 . . . . . : vivi.dyndns.org
    レコードの種類 . . . : 1
    Time To Live  . . . .: 13
    データの長さ . . . . : 4
    セクション . . . . . . . : 回答
    A (ホスト) レコード. . . : 220.157.170.199

■nslookupコマンドを使ってfacebook.comのIPアドレスを確認する

C:\temp>nslookup
DNS request timed out.
    timeout was 2 seconds.
既定のサーバー:  UnKnown
Address:  192.168.1.1

> facebook.com
サーバー:  UnKnown
Address:  192.168.1.1

権限のない回答:
名前:    facebook.com
Addresses:  2a03:2880:10:1f02:face:b00c:0:25
          2a03:2880:10:8f01:face:b00c:0:25
          2a03:2880:2110:3f01:face:b00c::
          66.220.149.11
          66.220.158.11
          69.171.224.37
          69.171.229.11
          69.171.242.11

nslookupはDNSサーバへのIPアドレスを問い合わせを手動で実行することができる。
facebook.comに対応するIPアドレスは5つあって、IPアドレスを複数登録することで負荷分散を行なっていることがわかる。

IPアドレスがわかってもホスト名がわからないとき

C:\temp>nslookup
DNS request timed out.
    timeout was 2 seconds.
既定のサーバー:  UnKnown
Address:  192.168.1.1

>  69.171.242.11
サーバー:  UnKnown
Address:  192.168.1.1

名前:    www-10-02-ash3.facebook.com
Address:  69.171.242.11

「>IPアドレス」でPTRレコードの問い合わせを行うことができる。

■ポート番号について
ポート番号は、PCやサーバ上で稼働しているアプリケーションを識別するための情報。
「8081番ポートで起動しているjboss」,「8082ポートで起動しているjboss」などと、
同じサーバの中で入り口を分けて使用することがある。

ウェルノウンポート(0~1023)

登録済みポート(1024~49151)

■ポート番号を確認するコマンド

netstat -n

C:\temp>netstat -n

アクティブな接続

  プロトコル  ローカル アドレス          外部アドレス        状態
  TCP    127.0.0.1:49453        127.0.0.1:6999         TIME_WAIT
  TCP    127.0.0.1:49511        127.0.0.1:49512        ESTABLISHED
  TCP    127.0.0.1:49512        127.0.0.1:49511        ESTABLISHED

■TCP/IPの設定を確認する

C:\temp>ipconfig /all

■IPアドレスを解放する

C:\temp>ipconfig /release

■IPアドレスを再取得する

C:\temp>ipconfig /renew

■デフォルトゲートウェイとは、同じネットワーク内のルータのこと

■DHCPサーバがクライアントにIPアドレスを割り当てる仕組み
①DHCPDISCOVER:問い合わせブロードキャストしてDHCPサーバを探しだす。
②DHCPOFFER:DHCPDISCOVERを受け取ったDHCPサーバが、使えるアドレスを選択してDHCPOFFERをクライアントに返す
③DHCPREQUEST:クライアントがDHCPOFFERを受け取ると、DHCPREQUESTをブロードキャストしてIPアドレスを要求する。

■ブロードキャストはルータを超えられない
→でも一つのネットワークに1台のDHCPサーバを置くのは非効率。
→DHCPリレーエージェントという機能を使って、複数のネットワークのDHCPクライアントに対してIPアドレスを配布する仕組み。
→ブロードキャストされたDHCPDISCOVERをユニキャストに変換してDHCPサーバに転送している。

■IPアドレスまたはホスト名への通信経路を確認する

C:\temp>tracert www.facebook.com

④DHCPACK:DHCPサーバが要求されたIPアドレスの使用を了承する。


■もっとわかりやすく勉強したいとき・・・