Genexus備忘録(実装編)

※量が増えてスクロールが面倒になったので記事を分割した。
→[操作編](https://qiita.com/aoi_erimiya/items/836b9b56c6e1854eaae3)

##文法
なんかパスカルケースっぽい。
VBに近い文法に見える。
Genexus上で書いたコードの実態は、*.cfsファイルに保存されているようだ。

[関数一覧？](http://wiki.genexus.jp/hwiki.aspx?%e3%83%97%e3%83%ad%e3%82%b7%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bc%e5%86%85%e3%81%ae%e9%96%a2%e6%95%b0,)

###演算子

残念ながら++とか%はない
!はあるけど別の意味(リテラル)なので注意

```
&i = 0
&i = &i + 1

// 余りはMod
If Mod(&i, 3) = 0
   ...
EndIf

// ～以外は<>
If &a <> 0
 ...
EndIf

// 比較演算子は==ではなく=
If Not &a = 1
 ...
EndIf
```

###リテラル
!を付けないと、内部的に文字列がため込まれていくらしい...。
再利用か何か狙ってるのだろうか？詳細は不明。

```
hoge.str = 'hage'
hoge.str = !'page'
```

### If
ElseIfはないらしい

```
If &a <> 0
 ...
EndIf
```

### Case

```
Do Case
  Case &a = !'a'
    ...  
  Case &a = !'b'
    ...
  OtherWise
    ...
EndCase
```

###イベント構文
ユーザー定義イベントに数字を併記すると、対応するファンクションキー押下時に処理を割り当てることができる。
ただし、ブラウザ標準の機能が死ぬため注意。F12の開発者ツールとか。

```
// 起動時
Event Start
EndEvent

// 更新時(ユーザー定義イベント後も動いちゃう)
Event Refresh
EndEvent

// ユーザー定義イベント(F4割り当て)
Event 'sel' 4
EndEvent
```

##機能的なところ

###コード埋め込み？
地味に、他言語のコードを直接埋め込めるっぽい。
おそらくは、最初にプロジェクト設定で選択した言語限定だろうけど。

```
java for (int i = 0; i < 5; i++) {
java ...
java }
```

シングルクォートで囲っているのは、ユーザー定義イベント。
配置したボタンの名称を変更するには
１：プロパティから「On click Event」で「New」を選ぶ。
２：captionプロパティを編集する。

### Enum的なもの
ドメインを使う。
ナレッジベースナビゲータからドメインを開いたら一覧が見れる。
プロパティのEnum Valuesから専用エディタを開ける。

###キャスト
変換できないものがあったら0を返すらしい。

```

val(&str)
str(&num)
```

### SDT
Newしてあげないと、メモリ割り当てされない。
For Eachとかで回してListにpushする時は
ループ毎にNewしないと、参照渡しになる故。

```
&HOGE_SDT = New()
```

一度定義したIDを変更する場合は、プロパティ上から編集する。
Structureタブで直接編集すると、元々のIDとは別項目として、新規項目扱いされるらしい。
「Is Collection」にチェックを入れると、リストとして扱ってくれる。


### 例外
基本は例外という考え方はなく
システムエラー以外は空文字とか0とかにキャストしてくるらしい。


### Procedure呼び出し
Callで外から呼び出せる。
パラメータはRules枠で定義できる。

Rulesのパラメータ定義

```
parm(in: &hoge, out: &hage);
```

呼び出し方A

```
hogehogeProc.Call(&hoge, &hage)
```

呼び出し方B

```
&hage = hogehogeProc.Udp(&hoge)
```

最後のout項目限定で、Udp()って呼び出し方すると戻り値として返してくれる。
複数out項目を定義しても、Udp()で返ってくるのは最後の一個だけ。
つまり、他プロシージャの呼び出しの引数に渡せる。

```
hageProc.Call(hogehogeProc.Udp(&hoge))
```

###プロシージャに複数メソッド定義
Rulesでのパラメタ定義が使えなくなるけど、こんな風に定義できる。

```
Stub sel(in:&cd, out:&rtn)
  ...
EndStub

Stub del(in:&cd, out:&rtn)
  ...
EndStub
```

呼び出し側

```
hoge.sel(&cd, &rtn)
hoge.del(&cd, &rtn)
```

###サブルーチン
privateメソッドを定義。
外部からは呼べない。
戻り値を返せないので、サブルーチン内部ではグローバル変数を書き換える。

```
Sub 'calc'
  Do 'multiple'
EndSub
```

###セッション
SDTとかのデータの受け渡しはセッションを使える。
先にVariablesタブでWebSessionの変数を定義しとかないと補完が聞かないのでつらい。

```
// シリアライズ
&webSession.Set('key', &sdts.ToXml())

// デシリアライズ
&sdts.FromXml($webSession.Get('key'))
```
## DB周り
### 検索条件を絞る
when句を使うと、条件を満たす場合のみ、where句を付与できるようになる。
Likeと検索文字列を'%'と結合させて、前方一致やら部分一致やらを実現できる。

```
For Each m_shohin Order ms_shohin_cd
    where ms_shohin_cd = &ms_shohin_cd when not &ms_shohin_cd.IsEmpty()
    where ms_shohin_nm like '%' + &ms_shohin_nm + '%' when not &ms_shohin_nm.IsEmpty()
    where ms_shohin_price > &ms_shohin_price when &ms_shohin_price > 0
EndFor

```

キーさえ一意なら、テーブル名の指定もいらなかったりする。

```
For Each
  where ms_shohin_cd = &ms_shohin_cd when not &ms_shohin_cd.IsEmpty()
EndFor
```

外部キーの場合は、逆にテーブル名を指定してあげないと、エラーになる。

```
// m_shohin_hd, m_shohin_dtlだけを結合したいが、
// テーブル名を省くとms_shohinもくっつけようとしてリレーションNGになる
For Each m_shohin_hd, m_shohin_dtl
  where ms_shohin_cd = &ms_shohin_cd when not &ms_shohin_cd.IsEmpty()
　where m_shohin_hd_del_flg = False
  where m_shohin_dtl_del_flg = False
EndFor
```

集約はたぶんこう(未検証)

```
For each
  where ms_shohin_del_flg = False
  &TotalAmt = Sum(ms_shohin_category, ms_shohin_sel_div = !'1')
EndFor
```

### Insert/Update/Delete

```
// INSERTだけならこれで
New
	ms_shohin_cd = &ms_shohin_cd
	ms_shohin_nm = &ms_shohin_nm
	ms_shohin_price = &ms_shohin_price
Endnew

For Each m_shohin
	where ms_shohin_cd = &ms_shohin_cd
	ms_shohin_nm = &ms_shohin_nm
	ms_shohin_price = &ms_shohin_price
when none
	New
		ms_shohin_cd = &ms_shohin_cd
		ms_shohin_nm = &ms_shohin_nm
		ms_shohin_price = &ms_shohin_price
	Endnew
EndFor

For Each m_shohin
	where ms_shohin_cd = &ms_shohin_cd
	Delete
	&rtn = 1
when none
	&rtn = 2
EndFor
```

##トラブルシューティング
・was wrong...とか言われる
→似たようで互換性がない型が、おそらくAutodefined Variablesで登録されている

・Numeric = Varcharとか言われる
→型が間違っている、Autodefined Variablesのせいなのか、変数定義してないからかは要確認。意図的にやってるならキャストする。

・日付はどうやって初期化するの
→.SetEmpty()を使う

・DBの再編成がこける
→型変更とかのケースでやってしまいがち。DDLはgeneXusが勝手に発行するので、手動でDDLを発行せず、再編成プログラムに任せること。

・実行時にCaused by: com.genexus.GXRuntimeException: org.postgresql.util.PSQLException: ERROR: portal "C_65" does not exist
→整数項目にマイナスつっこもうとしてたり、色々…

・java.lang.reflect.InvocationTargetExceptionが起きる
自分で書いたコードじゃないんだからリフレクションエラー言われてもわけわからねーよぉ！！

ナレッジベースのフォルダ階層の中に出力されたjavaファイル見て、javaベースでロジック追いかけるのが一番早い気がする。
C:\GenKB\JavaModel\web\ws\***\****.java

・portal "C_[0-9]+" does not exist
HTTPステータス 500 - java.lang.reflect.InvocationTargetException

```
Caused by: com.genexus.GXRuntimeException: org.postgresql.util.PSQLException: ERROR: portal "C_136" does not exist
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2101)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1834)
	at org.postgresql.core.v3.QueryExecutorImpl.fetch(QueryExecutorImpl.java:2036)
	at org.postgresql.jdbc2.AbstractJdbc2ResultSet.next(AbstractJdbc2ResultSet.java:1821)
	at com.genexus.db.driver.GXResultSet.next(Unknown Source)
	at com.genexus.db.ForEachCursor.next(Unknown Source)
	at com.genexus.db.DataStoreProvider.readNext(Unknown Source)
```

### NG
```
For &Upd_SDT in &Upd_SDTs
	For Each
		where CrtDate = &ServerNow
		OracleAcntgDataKbn = !'9'
		Commit
	EndFor	
EndFor
```

### OK
```
For &Upd_SDT in &Upd_SDTs
	For Each
		where CrtDate = &ServerNow
		OracleAcntgDataKbn = !'9'
	EndFor
	Commit	
EndFor

For &Upd_SDT in &Upd_SDTs
	For Each
		where CrtDate = &ServerNow
		OracleAcntgDataKbn = !'9'
	EndFor	
EndFor
Commit
```

## ほか
続きは随時追記していく方式で。