2016-05-16 3 views
0

GrailsコマンドオブジェクトでGORMドメインオブジェクトを使用すると、コマンドオブジェクトはsave()メソッドを呼び出していなくても自動的にドメインオブジェクトへの変更をコミットします。Grailsのコマンドオブジェクトは、デフォルトでドメインオブジェクトへの変更をコミットするのはなぜですか?

コマンドオブジェクトのGORMオブジェクトにバインドしますが、データベースに変更を保存したりコミットしたりする必要はありません。私のコントローラやサービスが例外をスローすると、トランザクションのロールバックが必要になります。

私は以下のアノテーションで必要な動作を強制することができますが、私はそれを難し​​い方法でやっているように感じています。

Controller Class = @Transactional(readOnly = true) 
Controller action method = @Transactional 
Command Object Class = @Transactional(readOnly = true) 
Service Class = @Transactional 

これらの注釈をすべて追加しない限り、Grailsドメインオブジェクトはコマンドオブジェクトによって自動的にコミットされるはずですか?

答えて

1

これはコマンドオブジェクトに固有のものではなく、コントローラアクションの一般的な機能です。デフォルトでは、セッション中のオープン・セッション・パターンがアクティブで、Hibernateセッションが作成され、アクションが実行される前にスレッドローカルにバインドされ、アクションが完了した後でフラッシュされ、閉じられます。データベースから取得された永続インスタンス(明示的に問合せのため、またはデータ・バインディング時に暗黙的に)は、オープン・セッションに接続されたままとなり、セッションがフラッシュされるとダーティチェックされます。変更されたインスタンスの変更は、save()呼び出しの有無にかかわらず、他のキューに入れられたアクションと共にフラッシュされます。

メソッド(またはクラス)全体をトランザクションと読み取り専用にすることはおそらく過剰です。より直接的なアプローチは、インスタンスを読み込み専用として取り出すことである。 get()の代わりにread()を使用し、条件クエリなどを実行するときにreadOnlyメソッドを呼び出すか、それぞれに対してdiscard()メソッドを呼び出すことによって、変更されたインスタンスを「切り離す」。もう1つの選択肢は、アクションの終わりにセッションをクリアすることです。自動的にフラッシュするものはありません。あなたが明示的にsave()を呼び出すときに「読み取り専用」モードで取得したインスタンスは、その変更を永続化することができますが、Hibernateは自動的にそれらのインスタンスのために何もしません

AnyDomainClass.withSession { it.clear() } 

注意、それだけで起こります。

+0

しかし、コマンドオブジェクトにバインドされたオブジェクトは、コマンドオブジェクトの後で、アクションの最後ではなくコントローラアクションの最初の行の前に暗黙的にコミットされます。上記のように注釈を付けない限り、私はコマンドオブジェクト内で1つのトランザクションを取得し、アクション内で別のトランザクションを取得するように見えます。 – DAC

+0

これを示す小さなテストアプリを作成できますか?私は見てみたいので、Githubや他のオンラインリポジトリに取り組んでいたり、コードを解凍してメールしてもらえればうまくいけばそれを追うことができます。それが本当のバグなら、それを報告するのが良いでしょう。 –

+0

OK、見つけました!デモアプリケーションには同じ動作がありませんでした。検証に役立つコマンドオブジェクトにUtilServiceを注入することで問題が発生していることが判明しました。そのサービスは** @Transactional(readOnly = true)という注釈付きではないので、トランザクションを開始し、そのトランザクションをコミットしたコントローラアクションに戻ります。 UtilService '@Transactional(readOnly = true)に注釈を付けると、問題が解決されました。 – DAC

関連する問題