質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Q&A

解決済

2回答

11291閲覧

(Java)トランザクションでコミット前のデータを取得できない。

_cocapeach

総合スコア20

Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

0グッド

1クリップ

投稿2018/10/02 06:57

編集2018/10/02 14:30

お世話になります。
トランザクションを使用したデータの操作で、
コミット前のデータが取得できない場合とできる場合があるのですが、
原因がよくわからないため、ご教示頂けますと幸いです。

各バージョン情報 Java:1.8 フレームワーク:Spring Boot ORM:JPA ORMバージョン:spring-boot-starter-data-jpa-2.0.0.BUILD-SNAPSHOT DB:Oracle DBバージョン:12.2.0.1.0

①下記のような場合、コミット前のデータ(今からコミットしようとしているデータ)が取得できます。

//Controller TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { hogeRepository.updateHoge( form.getId(), form.getComment() ); } catch(Exception e) { transactionManager.rollback(status); } //上記(updateHoge)で流したコミット前のデータ(DBに反映されていない最新のデータ)を取得できる。 Hoge hoge = hogeRepository.findById(form.getId()); transactionManager.commit(status);

②updateHogeの前にfindメソッドがあると、古いデータしか取得できない。

//Controller TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); //updateHogeメソッド前にfindByIdメソッドがあると、updateHoge実行後にnewHogeの最新データが入らない。 Hoge oldHoge = hogeRepository.findById(form.getId()); try { hogeRepository.updateHoge( form.getId(), form.getComment() ); } catch(Exception e) { transactionManager.rollback(status); } //oldHogeと同じデータしか取得できない。 Hoge newHoge = hogeRepository.findById(form.getId()); transactionManager.commit(status);
//Repository public interface HogeRepository extends JpaRepository<Hoge, Long>, JpaSpecificationExecutor<Hoge> { @Modifying @Query("UPDATE Hoge SET comment=:comment WHERE id = :id") Integer updateHoge(@Param("comment") String comment, @Param("id") Long id); }

・試してみたこと①

TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { hogeRepository.updateHoge( form.getId(), form.getComment() ); //コミット前のデータが取得できました。 Hoge hoge1 = hogeRepository.findById(form.getId()); } catch (Exception e) { } //コミット前のデータが取得できました。 Hoge hoge2 = hogeRepository.findById(form.getId()); try { //コミット前のデータが取得できました。 Hoge hoge3 = hogeRepository.findById(form.getId()); hogeRepository.updateHoge( form.getId(), form.getComment() ); //コミット前のデータが取得できました。 Hoge hoge4 = hogeRepository.findById(form.getId()); } catch (Exception e) { } //コミット前のデータが取得できました。 Hoge hoge5 = hogeRepository.findById(form.getId());

試してみたこと②(試してみたこと①の処理を逆にしてみました)

TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { //古いデータが取得されました。 Hoge hoge3 = hogeRepository.findById(form.getId()); hogeRepository.updateHoge( form.getId(), form.getComment() ); //古いデータが取得されました。 Hoge hoge4 = hogeRepository.findById(form.getId()); } catch (Exception e) { } //古いデータが取得されました。 Hoge hoge5 = hogeRepository.findById(form.getId()); try { hogeRepository.updateHoge( form.getId(), form.getComment() ); //古いデータが取得されました。 Hoge hoge1 = hogeRepository.findById(form.getId()); } catch (Exception e) { } //古いデータが取得されました。 Hoge hoge2 = hogeRepository.findById(form.getId());

試してみたこと③

TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { //古いデータが取得されました。 Hoge hoge1 = hogeRepository.findById(form.getId()); hoge1.setComment(form.getComment()); hoge1.save(); //コミット前のデータが取得されました。 Hoge hoge2 = hogeRepository.findById(form.getId()); } catch (Exception e) { }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubytomato

2018/10/02 12:49

差し支えなければ使用しているJavaのバージョン、フレームワーク,ORMの種類とそのバージョンを質問文に明記してください。
_cocapeach

2018/10/02 13:07

rubytomato様 お世話になります。 Java:1.8 フレームワーク:Spring Boot ORM:JPA バージョン:spring-boot-starter-data-jpa-2.0.0.BUILD-SNAPSHOT バージョンについてはこの回答であっていますでしょうか?
rubytomato

2018/10/02 13:28

追記ありがとうございます。追記して頂いた情報は質問文の方に書いていただけると他のユーザーにも目にとまりやすので回答が付きやすくなると思います。またこれも差し支えなければDBの種類とバージョンもお願いします。それから1点気になったことがありましたのでご確認いただきたいのですが@Queryに記述しているJPQLを"UPDATE Hoge h SET h.comment=:comment WHERE h.id = :id"のようにするとどうでしょうか
_cocapeach

2018/10/02 13:34

rubytomato様 有難う御座います。質問に追記致しました。 JPQLの修正ですが、明日検証させて頂きます。 update文自体は、コミット後に問題なく反映されているのですが、update文に原因がある可能性はありますでしょうか。
_cocapeach

2018/10/02 14:31

rubytomato様 ご丁寧に有り難うございます。参考ページ拝見させて頂きます。Springのタグを追加致しました。JPAタグは見つかりませんでした。
_cocapeach

2018/10/02 14:45

rubytomato様 もう一つの参考ページも有難うございます。仕様なのですね。。@Modifyingも明日試してみます。遅くまで本当に有難うございます。
_cocapeach

2018/10/03 01:54

rubytomato様 おはようございます。 @Modifying(clearAutomatically = true)とすることで、無事に意図した動きになりました。本当に有難う御座いました。
guest

回答2

0

自己解決

//Repository public interface HogeRepository extends JpaRepository<Hoge, Long>, JpaSpecificationExecutor<Hoge> { @Modifying(clearAutomatically = true) @Query("UPDATE Hoge SET comment=:comment WHERE id = :id") Integer updateHoge(@Param("comment") String comment, @Param("id") Long id); }

clearAutomatically = true

上記のオプションを加えることで、意図した動きとなりました。
ご回答下さった皆さま有難う御座いました。

投稿2018/10/03 01:56

_cocapeach

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2018/10/03 02:05

Java方面には疎くて、回り道させてしまいましたね。すみません。 springでのアノテーションに関することだったんですね。 springタグを追加してもらうと、後学者に役立つと思います。
_cocapeach

2018/10/03 02:08

sazi様 いえ、私の質問もわかりにくい書き方でした。 説明不足で混乱させてしまい、申し訳ありませんでした。 今後とも、機会がありましたら、宜しくお願い申し上げます。
guest

0

更新と取得が別トランザクションなら、当然更新がコミットされるまで取得はできません。
同じトランザクション内で行う必要があります。

投稿2018/10/02 08:00

sazi

総合スコア25138

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_cocapeach

2018/10/02 08:03

sazi様 ご回答頂き、誠に有難う御座います。 無知で大変恐縮なのですが、この場合、なぜ更新と取得が別トランザクションになっているのでしょうか? また、①で取得できて②で取得できないというのは、①は更新と取得が同じトランザクションになっているということでしょうか?
sazi

2018/10/02 08:09

>TransactionDefinition def = new DefaultTransactionDefinition(); 上記でNewしているので別トランザクションになっているじゃないかと判断しました。 クラスの内容などはわからないので推測ですけど。
_cocapeach

2018/10/02 08:15

sazi様 お返事有難う御座います。 >TransactionDefinition def = new DefaultTransactionDefinition(); 上記のコードでトランザクションを生成する以前に別トランザクションが存在している可能性があるということでしょうか?
_cocapeach

2018/10/02 08:26 編集

sazi様 何度も申し訳ありません。 >TransactionDefinition def = new DefaultTransactionDefinition(); 上記のコードはメソッド内で一度しか使用していないのですが、 別トランザクションになってしまっていた場合、 上記のコード以降のDB処理(取得、更新)を同一のトランザクションで行いたい場合、どのようにすればよいでしょうか? 質問ばかりで大変恐縮ですが、ご教示頂けますと幸いです。 宜しくお願い致します。
sazi

2018/10/02 08:50 編集

それぞれのメソッドでNewしてしまうから別トランザクションになるわけで、defをnewで生成するということは、それ自体がDBにコネクションしてトランザクションを開始するという事になるのではないでしょうか。なので、使いまわす必要があるかと。 要は①や②の処理では同じインスタンスのdefを参照するようにする
sazi

2018/10/02 08:38 編集

可能なら、試しに別メソッドではなく①の続きに②を記述してみれば、検証できると思います。
_cocapeach

2018/10/02 08:45

sazi様 お返事有難う御座います。 私の質問の仕方がわかりにくかったのかもしれません。 同じクラス内に①と②の処理を両方記載しているのではありません。 ②しか記述していません。 そのため、Newも同じクラス内で一度しか行なっておりません。 なので、別トランザクションになってしまっている場合、原因がわかりません。 >可能なら、試しに別メソッドではなく①の続きに②を記述してみれば、検証できると思います。 行なってみますので、しばらくお待ちください。
sazi

2018/10/02 08:53 編集

>TransactionDefinition def = new DefaultTransactionDefinition(); 一つにする場合、上記部分は1回だけですので、念のため。
_cocapeach

2018/10/02 09:03

追記致しましたが、sazi様の意図している通りに出来ていますでしょうか? 試してみた結果、やはり、updateメソッドの前にfindメソッドがあるかないかで、その後のメソッド全ての取得できるデータが変わってしまいます。 この違いがわかりません。。。
sazi

2018/10/02 09:15

①と②が別メソッドでの話かと思っていましたがよく見ると、処理パターンの話ですね。 すみませんトランザクションは関係ありませんね。 form.getId()は処理対象のIDを取得する処理で、.updateHoge()は1番目のパラメータをKeyに2番目のパラメータで更新する処理で合っていますか?
_cocapeach

2018/10/02 09:17

sazi様 誤解を招いてしまい申し訳ありません。 >form.getId()は処理対象のIDを取得する処理で、.updateHoge()は1番目のパラメータをKeyに2番目のパラメータで更新する処理で合っていますか? その通りです。
_cocapeach

2018/10/02 09:20

.updateHoge()実行前にselect文を流すか、流さないかの違いで、 流すと、.updateHoge()以降にselectしたデータも全て古いデータを取得してしまいます。 .updateHoge()以前にselect文を流さなければ、.updateHoge()以降のselect文ではコミット前の最新データを取得できます。
sazi

2018/10/02 09:22

起きている現象から推測すると、.commit時点でしかDBへSQLを発行していないように見えます。 なので、.findById()で更新前の情報でリセットされる。
_cocapeach

2018/10/02 09:28

sazi様 お返事有難う御座います。 ②の場合のみ、そうなってしまうということでしょうか。 また、①、②両方において、.updateHoge()のSQL文はログに出力はされています。 ②の場合のみ、そうなってしまう場合、なぜ.updateHoge()の前に.findById()メソッドを記述してはダメなのでしょうか。。わかりません。。
sazi

2018/10/02 09:35

.updateHoge()でSQLが発行されているなら、.rollbackは発生していませんか?
sazi

2018/10/02 09:38

そもそも①で更新しているのに更新前のデータが取得されるというなら、updateが失敗していると云う事になるので。
_cocapeach

2018/10/02 10:52

sazi様 試してみたこと③を追記致しました。 .updateHoge()ではなく、古いデータを取得し、セッターで値をセットしたのちに、save()メソッドで更新すると、その後の取得もコミット前のデータが取得できました。 できれば、.updateHoge()のままにしたいのですが、 .updateHoge()との違いがわかりません。
sazi

2018/10/02 11:00

.updateHoge()で発行されているSQLはエラー無く実行されていたのですか? .updateHoge()の内部で何をやっているのか分かりませんので、何とも・・・
_cocapeach

2018/10/02 13:10

sazi様 お返事有難う御座います。 .updateHoge()は問題なく実行され、.commit()後はDBにも反映されております。 .updateHoge()ですが、下記のように定義しています。 public interface HogeRepository extends JpaRepository<Hoge, Long>, JpaSpecificationExecutor<Hoge> { @Modifying @Query("UPDATE Hoge SET comment=:comment WHERE id = :id") Integer updateHoge(@Param("comment") String comment, @Param("id") Long id); }
sazi

2018/10/03 01:52 編集

正直言ってよくわからないです。 DBサーバーとのトランザクション上では問題無いようですので、考えられるのは内部的なキャッシュの問題な気がします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問