ふぅ...これと格闘した別の日の後、私は物事の周りに私の腕を持っていると思います。この回答は、元の質問の説明よりも少し広い範囲をカバーしていますが、それは、Hibernateジェネレーターの問題を乗り越えた後にさらに多くの問題を見つけたためです。
問題#1:OracleのGUID()
の取得 値
Adam Hawkesの回答でカバーされているように、「GUID」Hibernateジェネレーターは保守されておらず、古いバージョンのOracle方言でのみ機能します。
ただし、「割り当てられた」Hibernateジェネレーターを使用する場合(つまり、Hibernateに主キーを自動生成させるのではなく、手動で設定する場合)、Oracle SYS_GUID()
から取得した値を挿入できます。 電話してください。
Hibernateの新しいOracleダイアレクトは「GUID」をシームレスにサポートしていませんが、これらの値を生成するために必要なSQLを理解しています。コントローラ内にいる場合は、次の方法でそのSQLクエリをフェッチできます。
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
代わりにドメインクラス内にいる場合でも、これを行うことができます...ただし、最初にgrailsApplicationへの参照を挿入する必要があります。おそらくコントローラーでこれを実行したいと思うでしょうが...これについては以下で詳しく説明します。
興味がある場合は、ここで返される実際の文字列(Oracleの場合)は次のとおりです。
select rawtohex(sys_guid()) from dual
このSQLを実行して、次のように生成されたID値をフェッチできます。
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
問題#2:実際にGrailsドメインオブジェクトでこの値を使用する
GrailsドメインクラスでこのGUID値を実際に使用するには、「割り当てられた」Hibernateジェネレーターを使用する必要があります。前述のように、これは、Grails / GORM / HibernateにIDを自動的に生成させるのではなく、独自のIDを手動で設定することを宣言します。この変更されたコードスニペットを、上記の元の質問のコードスニペットと比較してください:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
ドメインクラスで、「guid」を「assigned」に変更しました。また、「columns {}
」を削除する必要があることもわかりました。 「ブロックをグループ化し、すべての列情報を1レベル上に移動します(奇妙な)。
ここで、これらのドメインオブジェクトを作成しているコントローラーはどれでも、上記のようにGUIDを生成し、オブジェクトの「id
」にプラグインします。 "フィールド。GrailsScaffoldingによって自動的に生成されたコントローラーでは、関数は" save()
になります。 ":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
このロジックをドメインオブジェクト内の「beforeInsert()
」に直接配置してみてください。 "関数。これは間違いなくよりクリーンでエレガントですが、" beforeInsert()
でIDを設定できないGrailsの既知のバグがいくつかあります。 「適切に。残念ながら、このロジックをコントローラーレベルに維持する必要があります。
問題#3:Grails / GORM/Hibernateにこれを適切に保存させる
明白な真実は、Grailsは主に新しいアプリケーションを対象としており、レガシーデータベースのサポートはかなりむらがあります(ただし、公平に言えば、私が試した他の「動的」フレームワークよりも少しむらがありません em> )。 「割り当てられた」ジェネレーターを使用している場合でも、ドメインオブジェクトを永続化するときにGrailsが混乱することがあります。
そのような問題の1つは、「.save()
「呼び出しは、INSERTを実行する必要があるときにUPDATEを実行しようとすることがあります。上記のコントローラースニペットに、「insert: true
」を追加したことに注意してください。 ".save()
のパラメータとして" "呼び出し。これは、Grails / GORM / Hibernateに、UPDATEではなくINSERT操作を試行するように明示的に指示します。
これが正しく機能するためには、すべての星と惑星が整列している必要があります。ドメインクラスが「static mapping {}
「ブロックはHibernateジェネレーターを「assigned
」に設定しません "、および" version false
も設定します "、その後、Grails / GORM / Hibernateは依然として混乱し、INSERTではなくUPDATEを発行しようとします。
自動生成されたGrailsScaffoldingコントローラーを使用している場合は、「insert: true
」を使用しても安全です。 コントローラの「save()
」の「 "関数。この関数は、新しいオブジェクトを初めて保存するときにのみ呼び出されます。ユーザーが既存のオブジェクトを編集すると、コントローラーの" update()
代わりに"関数が使用されます。ただし、独自のカスタムコードのどこかで独自のことを行う場合は、" .save()
"を呼び出し、" insert: true
のみを渡します 「本当に初めての挿入の場合はパラメータ。
問題#4:Grails / GORM/Hibernateで自然キーを使用する
最後に、Oracle GUID値とは関係ありませんが、これらのGrailsの問題全般に関連しています。従来のデータベース(私が扱ってきたデータベースなど)で、一部のテーブルが主キーとして自然キーを使用しているとしましょう。 OWNER_TYPE
があるとします OWNER
のすべての可能な「タイプ」を含むテーブル 、およびNAME
列は、人間が読める形式の識別子であると同時に主キーでもあります。
Grails Scaffoldingでこれを機能させるには、他にいくつかのことを行う必要があります。 1つには、自動生成されたビューは、ユーザーが新しいオブジェクトを作成しているときに画面にIDフィールドを表示しません。 IDのフィールドを追加するには、関連するビューにHTMLを挿入する必要があります。フィールドに「id
」という名前を付けると "の場合、自動生成されたコントローラーの" save() "関数は、この値を" params.id
として受け取ります。 "。
次に、自動生成されたコントローラーの「save()
「関数はID値を適切に挿入します。最初に生成されたとき、「save()」はビューによって渡されたCGIパラメータからドメインオブジェクトをインスタンス化することから始まります:
def ownerTypeInstance = new OwnerType.get( params )
ただし、これはビューに追加したIDフィールドを処理しません。それでも手動で設定する必要があります。ビューでHTMLフィールドに「id
」という名前を付けた場合 "の場合、" save()
で利用可能になります "as" params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
ケーキですねおそらく「問題#5」は、最初にSpring Web MVC(またはバニラJSP)を使用してCRUDインターフェイスを手動で作成するのではなく、なぜこのような苦痛を乗り越えたのかを理解しているのでしょう。 :)