[Seasar-user:22012] Re: 一意制約違反発生時の更新処理について

ohara [E-MAIL ADDRESS DELETED]
2014年 12月 10日 (水) 10:16:42 JST


金子様

小原ともうします

http://stackoverflow.com/questions/10338789/upsert-updateinsert-in-postgresql-via-jdbc-jpa-returning-strange-row-count
にあるように
http://www.postgresql.org/docs/9.1/interactive/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING
のmerge_dbのような関数を作ることで回避可能ではないでしょうか?
Java側のソースが簡素になると思います

手元に環境がないため試せていないため
的外れになってしまっていたら申し訳ありません


2014年12月10日 9:44 あきやまじろう <mayama0130 @ gmail.com>:

> 金子様
>
> あきやまです。
>
> ご意見ありがとうございます。
>
> > インサート時に一意制約違反が出たらアップデートする順番という
> > ことですが、アップデートを先に実行してアップデートの件数が0だったらインサートという順番では、何かまずいのでしょうか??
> >
> > 自分が関わった案件だと、アップデートを先に発行しているパターンしか見たことがないので、確認させていただきました。
> 上記の方法も検討しましたが、複数スレッドで同時に更新をかけた場合、
> 更新件数が0件となり、その後の登録処理で一意制約違反が発生する結果となりました。
>
>
>
>
> 2014年12月10日 1:46 Yuichi Kaneko <yuichikaneko @ gmail.com>:
>
> 金子と申します
>>
>> 横から失礼します。
>>
>> インサート時に一意制約違反が出たらアップデートする順番ということですが、アップデートを先に実行してアップデートの件数が0だったらインサートという順番では、何かまずいのでしょうか??
>>
>> 自分が関わった案件だと、アップデートを先に発行しているパターンしか見たことがないので、確認させていただきました。
>>
>>
>>
>> 2014/12/10 1:00、Koichi Kobayashi <koichik @ improvement.jp> のメッセージ:
>>
>> > 小林 (koichik) です。
>> >
>> > そういえばPostgreSQLはそういうものだと以前も
>> > 話題になってましたね。
>> >
>> > であるなら、Java側でどうしようとトランザクションを
>> > コミットすることはできないので、ロールバックと
>> > マークされること自体は全く問題ないと思います。
>> >
>> > となると、insertはupdateとは別のトランザクションで
>> > 実行することになるのでは。
>> >
>> >>>> 4.BテーブルのレコードをSELECT FOR UPDATEで取得
>> >>>> →取得できなかった場合、レコードを登録
>> >>>> →一意制約違反が発生した場合、レコードを更新
>> >
>> > の2行目の登録処理をRequiresNewの別メソッドに
>> > してみてはどうでしょうか。
>> > もし一意制約違反が発生しても、insertのトランザクションが
>> > ロールバックされるだけで、selectおよびupdateを行う
>> > トランザクション (B) には影響がないはずです。
>> >
>> > こうも細切れになるとトランザクションの意味が
>> > あるのか疑問ですが。。。
>> >
>> >
>> > なお、
>> >
>> >> 後者に関しては、以下の設定で試してみましたが、「既にロールバックとしてマーク」が出力され、期待通りの結果になりませんでした。
>> >
>> > に関しては、RequiresNew用のインターセプタに設定が
>> > 加えられていますが、「ロールバックとしてマーク」
>> > されているのはRequiredのトランザクション (一番外側の
>> > トランザクションA?) ですね。
>> > 何らかの例外がRequiredな宣言的トランザクションの
>> > 設定されたメソッドを突き抜けているものと思われます。
>> >
>> > 最初のメールからは「ロールバックとしてマーク」
>> > されているのはトランザクションBのことかと
>> > 思ってましたが、状況が違うようですね。
>> > ログからも内側のトランザクションはなぜか
>> > コミットできてますし。
>> > これは一意制約違反が発生が発生しないケース?
>> > それだと本題の解決には役に立たないような。。。
>> >
>> >
>> > On Tue, 9 Dec 2014 10:24:15 +0900, あきやまじろう <mayama0130 @ gmail.com>
>> wrote:
>> >
>> >> 小林 (koichik) 様
>> >>
>> >> あきやまです。
>> >>
>> >> ご回答ありがとうございます。
>> >>
>> >>> 対策としては、トランザクション境界の中で呼び出される
>> >>> メソッド (上記のbar()に相当) には宣言的トランザクションを
>> >>> 設定しないようにするか、特定の例外ではロールバックと
>> >>> マークされないように設定する方法があります。
>> >>> # 前者の方がよいかと思います。
>> >>>
>> >>> 後者については上記ドキュメントの「例外発生時の動作」に
>> >>> 説明があります。
>> >>> 実際に設定する場合はj2ee.diconをコピーして、
>> >>> S2にバンドルされているejb3tx.diconを参考に
>> >>> 一意制約違反発生時の例外でロールバックしないように
>> >>> 設定してください。
>> >> 前者に関してはPostgreSQLの場合、一意制約違反が発生した時点でトランザクションがアボートするらしく、
>> >> トランザクションを張りなおさないとDB操作ができない状態でした。
>> >> 後者に関しては、以下の設定で試してみましたが、「既にロールバックとしてマーク」が出力され、期待通りの結果になりませんでした。
>> >>
>> >> 【ソース】
>> >> class xxxService {
>> >>  public void execute() {
>> >>    try {
>> >>      insertTx();
>> >>    } catch (...) {
>> >>      update()
>> >>    }
>> >>  }
>> >>
>> >>  public void insertTx() {
>> >>  }
>> >>
>> >>  public void update() {
>> >>  }
>> >> }
>> >>
>> >> 【j2ee.dicon】
>> >> <component name="requiredTx"
>> >>  class="org.seasar.extension.tx.RequiredInterceptor"/>
>> >> <component name="requiresNewTx"
>> >>  class="org.seasar.extension.tx.RequiresNewInterceptor"/>
>> >> <component name="requiresNewEntityExistsCommitTx"
>> >>  class="org.seasar.extension.tx.RequiresNewInterceptor">
>> >>  <initMethod name="addCommitRule">
>> >>    <arg>@javax.persistence.EntityExistsException @ class</arg>
>> >>  </initMethod>
>> >> </component>
>> >> <component name="mandatoryTx"
>> >>  class="org.seasar.extension.tx.MandatoryInterceptor"/>
>> >> <component name="notSupportedTx"
>> >>  class="org.seasar.extension.tx.NotSupportedInterceptor"/>
>> >> <component name="neverTx"
>> >>  class="org.seasar.extension.tx.NeverInterceptor"/>
>> >>
>> >>
>> >> 【customeizer.dicon】
>> >> <component name="serviceCustomizer"
>> >>  class="org.seasar.framework.container.customizer.CustomizerChain">
>> >>  <initMethod name="addCustomizer">
>> >>    <arg>requiredTxCustomizer</arg>
>> >>  </initMethod>
>> >>  <initMethod name="addCustomizer">
>> >>    <arg>
>> >>      <component
>> >> class="org.seasar.framework.container.customizer.AspectCustomizer">
>> >>        <property
>> >>
>> name="interceptorName">"j2ee.requiresNewEntityExistsCommitTx"</property>
>> >>        <property name="pointcut">"insertTx"</property>
>> >>      </component>
>> >>    </arg>
>> >>  </initMethod>
>> >> </component>
>> >>
>> >> 【アプリケーションログ】
>> >> org.seasar.framework.exception.SIllegalStateException:
>> >> [ESSR0308]既にロールバックとしてマークされています
>> >>  at
>> >>
>> org.seasar.extension.jta.TransactionImpl.throwIllegalStateException(TransactionImpl.java:138)
>> >>  at
>> >>
>> org.seasar.extension.jta.TransactionImpl.assertActive(TransactionImpl.java:123)
>> >>  at
>> >>
>> org.seasar.extension.jta.TransactionImpl.enlistResource(TransactionImpl.java:452)
>> >>  at
>> >>
>> org.seasar.framework.util.TransactionUtil.enlistResource(TransactionUtil.java:64)
>> >>  at
>> >>
>> org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOut(ConnectionPoolImpl.java:371)
>> >>  at
>> >>
>> org.seasar.extension.dbcp.impl.DataSourceImpl.getConnection(DataSourceImpl.java:59)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.util.DataSourceUtil.getConnection(DataSourceUtil.java:51)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.manager.JdbcManagerImpl.getJdbcContext(JdbcManagerImpl.java:381)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.query.AbstractAutoUpdate.executeInternal(AbstractAutoUpdate.java:121)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.query.AutoUpdateImpl.executeInternal(AutoUpdateImpl.java:145)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.query.AbstractAutoUpdate.execute(AbstractAutoUpdate.java:90)
>> >>  at
>> >>
>> org.seasar.extension.jdbc.service.S2AbstractService.update(S2AbstractService.java:149)
>> >>  at jp.co.xxxx.dbaccess.service.xxxxService.insert(xxxxService.java:66)
>> >>  at
>> >>
>> jp.co.xxxx.dbaccess.service.xxxxService$$EnhancedByS2AOP$$7ae165.$$insert2$$invokeSuperMethod$$(xxxxService$$EnhancedByS2AOP$$7ae165.java)
>> >>  at
>> >>
>> jp.co.xxxx.dbaccess.service.xxxxService$$EnhancedByS2AOP$$7ae165$$MethodInvocation$$insert21.proceed(MethodInvocationClassGenerator.java)
>> >>  at
>> >>
>> org.seasar.extension.tx.DefaultTransactionCallback.execute(DefaultTransactionCallback.java:58)
>> >>  at
>> >>
>> org.seasar.extension.tx.adapter.JTATransactionManagerAdapter.required(JTATransactionManagerAdapter.java:65)
>> >>  at
>> >>
>> org.seasar.extension.tx.RequiredInterceptor.invoke(RequiredInterceptor.java:50)
>> >>  at
>> >>
>> jp.co.xxxx.dbaccess.service.xxxxService$$EnhancedByS2AOP$$7ae165$$MethodInvocation$$insert21.proceed(MethodInvocationClassGenerator.java)
>> >>  at
>> >>
>> jp.co.xxxx.dbaccess.service.xxxxService$$EnhancedByS2AOP$$7ae165.insert2(xxxxService$$EnhancedByS2AOP$$7ae165.java)
>> >>  at
>> >>
>> jp.co.xxxx.dbaccess.service.xxxxServiceTest.testUpdateDelFlg1(xxxxServiceTest.java:75)
>> >>  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>> >>  at
>> >>
>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>> >>  at
>> >>
>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>> >>  at java.lang.reflect.Method.invoke(Method.java:597)
>> >>  at junit.framework.TestCase.runTest(TestCase.java:168)
>> >>  at
>> >>
>> org.seasar.framework.unit.S2FrameworkTestCase.doRunTest(S2FrameworkTestCase.java:519)
>> >>  at org.seasar.extension.unit.S2TestCase.doRunTest(S2TestCase.java:103)
>> >>  at
>> >>
>> org.seasar.framework.unit.S2FrameworkTestCase.runBare(S2FrameworkTestCase.java:308)
>> >>  at junit.framework.TestResult$1.protect(TestResult.java:110)
>> >>  at junit.framework.TestResult.runProtected(TestResult.java:128)
>> >>  at junit.framework.TestResult.run(TestResult.java:113)
>> >>  at junit.framework.TestCase.run(TestCase.java:124)
>> >>  at junit.framework.TestSuite.runTest(TestSuite.java:243)
>> >>  at junit.framework.TestSuite.run(TestSuite.java:238)
>> >>  at
>> >>
>> org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
>> >>  at
>> >>
>> org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
>> >>  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>> >>  at
>> >>
>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>> >>  at
>> >>
>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>> >>  at java.lang.reflect.Method.invoke(Method.java:597)
>> >>  at
>> >>
>> jp.co.dgic.eclipse.jdt.internal.junit.runner.DJUnitRunner.main(DJUnitRunner.java:49)
>> >>
>> >> 【s2.log】
>> >> 2014-12-09 10:13:27,911 DEBUG TransactionImpl -
>> >> トランザクションを開始しました。tx=[FormatId=4360, GlobalId=1418087601966/7, BranchId=]
>> >> 2014-12-09 10:13:27,911 DEBUG TransactionImpl -
>> >> トランザクションを開始しました。tx=[FormatId=4360, GlobalId=1418087601966/8, BranchId=]
>> >> 2014-12-09 10:13:27,920 DEBUG ConnectionPoolImpl -
>> >> 論理的なコネクションを取得しました。tx=[FormatId=4360, GlobalId=1418087601966/8,
>> BranchId=]
>> >> 2014-12-09 10:13:27,923 DEBUG AutoInsertImpl - insert into ・・・
>> >> 2014-12-09 10:13:27,935 DEBUG TransactionImpl -
>> >> トランザクションをコミットしました。tx=[FormatId=4360, GlobalId=1418087601966/8,
>> BranchId=]
>> >> 2014-12-09 10:13:27,935 DEBUG ConnectionWrapperImpl -
>> >> 論理的なコネクションを閉じました。tx=[FormatId=4360, GlobalId=1418087601966/8,
>> BranchId=]
>> >> 2014-12-09 10:13:27,958 DEBUG TransactionImpl -
>> >> トランザクションをロールバックしました。tx=[FormatId=4360, GlobalId=1418087601966/7,
>> BranchId=]
>> >>
>> >>
>> >> 2014年12月9日 3:48 Koichi Kobayashi <koichik @ improvement.jp>:
>> >>
>> >>> 小林 (koichik) です。
>> >>>
>> >>> おそらくですが、以下のドキュメントに書いてある
>> >>> ことかと思います。
>> >>>
>> >>> http://s2container.seasar.org/2.4/ja/tx.html
>> >>>
>> >>> 「トランザクションを開始した場合でも引き継いだ場合でも、
>> >>> このインターセプタが適用されたメソッドが例外をスローした場合は、
>> >>> 例外の種類に応じてトランザクションがロールバックされるように
>> >>> マークします。」
>> >>>
>> >>> つまり、
>> >>>
>> >>> public void foo() {
>> >>>  try {
>> >>>    bar();
>> >>>  } catch (...) {
>> >>>    ...
>> >>>  }
>> >>> }
>> >>>
>> >>> public void bar() {
>> >>>  throw ...;
>> >>> }
>> >>>
>> >>> foo()とbar()の両方に宣言的トランザクションが
>> >>> 適用される場合、foo()でトランザクションを開始して
>> >>> bar()はそのトランザクションの中で呼び出されますが、
>> >>> bar()がスローする例外によってfoo()で開始した
>> >>> トランザクションは「ロールバックされるようにマーク」
>> >>> されます。
>> >>> そのため、foo()でその例外をキャッチしても、
>> >>> foo()からリターンする際にトランザクションは
>> >>> ロールバックされます。
>> >>>
>> >>> 対策としては、トランザクション境界の中で呼び出される
>> >>> メソッド (上記のbar()に相当) には宣言的トランザクションを
>> >>> 設定しないようにするか、特定の例外ではロールバックと
>> >>> マークされないように設定する方法があります。
>> >>> # 前者の方がよいかと思います。
>> >>>
>> >>> 後者については上記ドキュメントの「例外発生時の動作」に
>> >>> 説明があります。
>> >>> 実際に設定する場合はj2ee.diconをコピーして、
>> >>> S2にバンドルされているejb3tx.diconを参考に
>> >>> 一意制約違反発生時の例外でロールバックしないように
>> >>> 設定してください。
>> >>>
>> >>>
>> >>> On Mon, 8 Dec 2014 19:02:32 +0900, あきやまじろう <mayama0130 @ gmail.com>
>> wrote:
>> >>>
>> >>>> お世話になっております。あきやまと申します。
>> >>>>
>> >>>> 一意制約違反発生時の更新処理について教えてください。
>> >>>>
>> >>>> [動作環境]
>> >>>> ・S2Container 2.4.45
>> >>>> ・PostgreSQL 9.0.13
>> >>>>
>> >>>> 現在、以下の処理を実装しようとしていますが、
>> >>>>
>> >>>> 1.トランザクション開始(トランザクションA)
>> >>>> 2.SELECT FOR UPDATEでAテーブルからレコードを取得
>> >>>> 3.トランザクション開始(トランザクションB)
>> >>>> 4.BテーブルのレコードをSELECT FOR UPDATEで取得
>> >>>> →取得できなかった場合、レコードを登録
>> >>>> →一意制約違反が発生した場合、レコードを更新
>> >>>> 5.トランザクションBをコミット
>> >>>> 6.CテーブルのレコードをSELECT FOR UPDATEで取得
>> >>>> →取得できなかった場合、レコードを登録
>> >>>> →一意制約違反が発生した場合、レコードを更新
>> >>>> 7.トランザクションCをコミット
>> >>>> 8.トランザクションAをコミット
>> >>>>
>> >>>> 項番4で一意制約違反が発生した場合にレコードを更新したいのですが、
>> >>>> 「既にロールバックとしてマークされています」とログ出力され、更新できなくなり困っています。
>> >>>> 項番1から8までをtry-catchして、項番1から再トライすれば、うまくいくのですが、
>> >>>> 項番6でも一意制約違反の可能性があり、その場合try-catchをネストしないといけないので、できれば避けたいです。
>> >>>> 何かいい対処方法があれば、ご教授の程よろしくお願いします。
>> >>>
>> >>>
>> >>> --
>> >>> {
>> >>>  name: "Koichi Kobayashi",
>> >>>  mail: "koichik @ improvement.jp",
>> >>>  blog: "http://d.hatena.ne.jp/koichik/",
>> >>>  twitter: "@koichik"
>> >>> }
>> >>>
>> >>> _______________________________________________
>> >>> Seasar-user mailing list
>> >>> Seasar-user @ ml.seasar.org
>> >>> https://ml.seasar.org/mailman/listinfo/seasar-user
>> >
>> >
>> > --
>> > {
>> >  name: "Koichi Kobayashi",
>> >  mail: "koichik @ improvement.jp",
>> >  blog: "http://d.hatena.ne.jp/koichik/",
>> >  twitter: "@koichik"
>> > }
>> >
>> > _______________________________________________
>> > Seasar-user mailing list
>> > Seasar-user @ ml.seasar.org
>> > https://ml.seasar.org/mailman/listinfo/seasar-user
>> _______________________________________________
>> Seasar-user mailing list
>> Seasar-user @ ml.seasar.org
>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>
>
>
> _______________________________________________
> Seasar-user mailing list
> Seasar-user @ ml.seasar.org
> https://ml.seasar.org/mailman/listinfo/seasar-user
>
>
-------------- next part --------------
HTML$B$NE:IU%U%!%$%k$rJ]4I$7$^$7$?(B...
URL: <http://ml.seasar.org/archives/seasar-user/attachments/20141210/7f41d809/attachment.html>


Seasar-user メーリングリストの案内