IBM iのGrails Plugin その1
IBM i(AS/400)のプラグインをGrailsの公式サイトで調べると、意外なことに3つも存在してます。GrailsとASの両方をやってる人がいて、さらにPluginまで作っている人がいると思うと、世界は広いなと思います。もし自分に英語力があったらコミュニケーションとってみたいです。
Grailsのプラグイン
■ibm iのカテゴリー
その中で、「Systemi Grails Domain Plugin 」の”既存DBのリバースエンジニアリング”が直ぐ目に付いたので、早速試してみました。本来ドメインありきのGrailsにとって、RDBありきではないので本筋とは違うカモですが、RDBをドメインに置き換えるとしても、”エイ!ヤー!”でフィールド(カラム)を引き込んでくれるだけでも、結構ありがたい気がします。
導入手順等はこちらを参考にさせて頂きました。
インストール
- 適当なアプリケーションを「grails create-app」で作成&カレントディレクトリ移動。
- 「grails install-plugin http://grails400utils.googlecode.com/files/grails-systemitools-0.4.zip」
- 追加されたコマンド
- 「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が出来るのですが、長くなってしまったので、次の日記に分けます。