ushidayの日記

主に「IBMi」のメモに・・・

IBM iのGrails Plugin その1

IBM i(AS/400)のプラグインGrailsの公式サイトで調べると、意外なことに3つも存在してます。GrailsとASの両方をやってる人がいて、さらにPluginまで作っている人がいると思うと、世界は広いなと思います。もし自分に英語力があったらコミュニケーションとってみたいです。
Grailsプラグイン
ibm iのカテゴリー

  • IBM i support
  • Systemi Grails Domain Plugin
  • jtopen

その中で、「Systemi Grails Domain Plugin 」の”既存DBのリバースエンジニアリング”が直ぐ目に付いたので、早速試してみました。本来ドメインありきのGrailsにとって、RDBありきではないので本筋とは違うカモですが、RDBドメインに置き換えるとしても、”エイ!ヤー!”でフィールド(カラム)を引き込んでくれるだけでも、結構ありがたい気がします。
導入手順等はこちらを参考にさせて頂きました。

インストール
  1. 適当なアプリケーションを「grails create-app」で作成&カレントディレクトリ移動。
  2. grails install-plugin http://grails400utils.googlecode.com/files/grails-systemitools-0.4.zip
  3. 追加されたコマンド
    • grails build-systemi-grails-domain (レガシーからのReverse engineering)
    • grails generate-picker (まだ調べてないです)
    • grails generate-systemi-all (controllerとviewの静的scaffold)
  4. grails compile」を実行
build-systemi-grails-domain

「build-systemi-grails-domain」は、AS/400内にある既存DB2のデータベースから、DomainClassにリバースエンジニアリングするコマンドです。コマンドを実行するとインタラクティブに”ホストのアドレス(IP等)”、”ASユーザー名”、”ユーザーパスワード”、”ライブラリー”、”ファイル名”、”DomainClass名”の順に要求されます。
(※実はこのDomainClass名の指定ですが、つい”create-domain-class”コマンドの癖で、先頭を小文字にしてしまいましたが、この後に出てくるgenerateコマンドのscaffoldで見事にコケました。忘れないようにDomainClass名の先頭を大文字にします。
しかし、コマンドを実行すると「Error executing script BuildSystemiGrailsDomain:〜」とエラーになってしまいました。その場合は「grails cleanをしてみて」という説明っぽいので、とりあえずcleanをしてみます。自分の場合は、cleanした後は、問題なく動きました。

grails build-systemi-grails-domain
------ 中略 ------
Please enter IP:
192.168.x.xxx
user:
USHIDA5035
password:
*******
library:
CSCFLE
file:
SHAMASF
Grails domain class name:
Employee
------ 中略 ------
     [move] Moving 7 files to C:\SAVF\grails\1.1.1\cscfle\src\templates\scaffolding
     [move] Moving 1 file to C:\SAVF\grails\1.1.1\cscfle\grails-app\taglib
     [echo]  be sure to modify grails-app/conf/DataSource.groovy to specify your System i settings eg:
     [echo]     dataSource {
     [echo]         pooled = true
     [echo]         url = "jdbc:as400://192.168.x.xxx/CSCFLE"
     [echo]         driverClassName = "com.ibm.as400.access.AS400JDBCDriver"
     [echo]         username = "USHIDA5035"
     [echo]         password = "*******"
     [echo]         dialect = org.hibernate.dialect.DB2400Dialect.class
     [echo]     }
Finished generation of domain

[echo]で表示されている様に、「$PROJECT_HOME/grails-app/conf/DataSource.groovy」を、以下の様に変更します。

dataSource {
    pooled = true
    url = "jdbc:as400://192.168.x.xxx/CSCFLE"
    driverClassName = "com.ibm.as400.access.AS400JDBCDriver"
    username = "USHIDA5035"
    password = "*******"
    dialect = org.hibernate.dialect.DB2400Dialect.class
}
environments {
	development {
	    dataSource {
	        dbCreate = "create-drop" // one of 'create', 'create-drop','update'
	    	url = "jdbc:as400://192.168.x.xxx/CSCDEV"    
	        loggingSql = true
	    }
	}
	test {
	    dataSource {
	        dbCreate = "update"
	    	url = "jdbc:as400://192.168.x.xxx/CSCTEST"    
            loggingSql = true
	    }
	}
	production {
	    dataSource {
	        //dbCreate = "update"
	    	url = "jdbc:as400://192.168.x.xxx/CSCFLE"    
	    }
	}
}

このようなDDS(Data Define Structure)から
(S:ゾーン10進、A:SBCS文字列、O:DBCS混合文字列、K:キー)

     A*****************************************************************
     A*   SHAMASF  社員マスター             04/08/20  CSC)Y.U     *
     A*****************************************************************
     A                                      UNIQUE
     A          R SHA00FR                   TEXT('社員マスター')
     A            SHA001         3S 0       COLHDG('社員コード')
     A*
     A            SHA002        16A         COLHDG('社員フリガナ')
     A            SHA003        16O         COLHDG('社員名')
     A            SHA004         8S 0       COLHDG('入社年月日')
     A            SHA005         1A         COLHDG('職務コード')
     A            SHA006        30O         COLHDG('項目')
     A            SHA007         3S 0       COLHDG('レベル係数')
     A            SHA008         7S 0       COLHDG('ダミー')
     A            SHA009         1A         COLHDG('削除フラグ')
     A*                                              D:削除
     A            SHA900         8S 0       COLHDG('作成日')
     A            SHA901         6S 0       COLHDG('作成時間')
     A            SHA902         8S 0       COLHDG('変更日')
     A            SHA903         6S 0       COLHDG('変更時間')
     A*KEY
     A          K SHA001                                              

このようなDomainClass(Employee.groovy)がリバースエンジニアリングされました。

class Employee implements Serializable {
	int id
	String sha002
	String sha003
	int sha004
	String sha005
	String sha006
	int sha007
	int sha008
	String sha009
	int sha900
	int sha901
	int sha902
	int sha903
	static constraints = {
		sha002(maxSize:16,nullable:false)
		sha003(maxSize:16,nullable:false)
		sha004(max:99999999,nullable:false)
		sha005(maxSize:1,nullable:false)
		sha006(maxSize:30,nullable:false)
		sha007(max:999,nullable:false)
		sha008(max:9999999,nullable:false)
		sha009(maxSize:1,nullable:false)
		sha900(max:99999999,nullable:false)
		sha901(max:999999,nullable:false)
		sha902(max:99999999,nullable:false)
		sha903(max:999999,nullable:false)
	}
	static final boolean ASSIGNED_KEY = true
	static final boolean COMPOSITE_KEY  = false
	static mapping = {
		table ('SHAMASF')
		version (false)
		id (generator:'assigned')
		columns {
			id (column:'SHA001',type:'int')
			sha002 (column:'SHA002', type:'string')
			sha003 (column:'SHA003', type:'string')
			sha004 (column:'SHA004', type:'int')
			sha005 (column:'SHA005', type:'string')
			sha006 (column:'SHA006', type:'string')
			sha007 (column:'SHA007', type:'int')
			sha008 (column:'SHA008', type:'int')
			sha009 (column:'SHA009', type:'string')
			sha900 (column:'SHA900', type:'int')
			sha901 (column:'SHA901', type:'int')
			sha902 (column:'SHA902', type:'int')
			sha903 (column:'SHA903', type:'int')
		}
	}
	boolean newEntity = false
	static transients = ['sha001', 'newEntity']
	def getSha001() {
		if (id) return id
		return 0
	}
	void setSha001 (def vlu) { id = vlu }
}

自分は、DomainClassの”mapping”とかよく解ってなかったのですが、こうやって結果で見ると非常に解り易いです。複合キーのファイルをリバースエンジニアリングすると「COMPOSITE_KEY=true」になって、idメソッドが「id (composite:['column1','column2'])」とリスト形式になっていました。
AS/400のファイルのキーが単一の整数フィールドだと、idとフィールドが2つにならないように、SetterとGetterがオーバーライドされているのにも感心しました。
grails generate-systemi-all」コマンドで静的scaffoldが出来るのですが、長くなってしまったので、次の日記に分けます。