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