3

StaleObjectStateException私は1 週間以上に苦労しており、問題を再現する簡単なアプリをここに投稿することにしました。

が楽観的ロックorg.hibernate.StaleObjectStateExceptionの例外であることは理解しています。さらに、楽観的ロックは、各エンティティ クラスのバージョン フィールドの使用に依存しています。

ここで、上記の例外をどのように再現したかを説明します。サンプル アプリには、次のようなMemberエンティティ クラスがあります。

@RooJavaBean
@RooToString
@RooJpaEntity
public class Member {

    @OneToOne(cascade=CascadeType.ALL)
    private Address address;
    //id, version fields as well as mutator/accessors are located in a separate Roo ITD/aspect  
}

Addressエンティティクラスは次のとおりです。

@RooJavaBean
@RooToString
@RooJpaEntity
public class Address {

    private String formattedAddress;
    private double lng;
    private double lat;
    //id, version fields as well as mutator/accessors are located in a separate Roo ITD/aspect 
}

新しい/管理されていない Address インスタンス管理された Member インスタンスを使用してServiceImpl、次のようにクラス内のメンバーのアドレスを更新しようとしています。

@Override
public void updateMemberAddress(Member member, Address address) {
    long addressId = member.getAddress().getId();
    address.setId(addressId);
    updateAddress(address);
}

テストクラスは次のとおりです。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext*.xml")
@TransactionConfiguration(defaultRollback = false)
public class AddressIntegrationTest {

    @Autowired
    private Service service;

    @Before
    @Transactional
    public void testInsertOneMember() {
        Member member = new Member();
        Address address = new Address();
        address.setFormattedAddress("Eiffel Tower, Paris");
        address.setLat(48.005);
        address.setLng(3.288);
        member.setAddress(address);
        service.saveMember(member);
    }

    @Test
    @Transactional
    public void testUpdateAddress() {
        Member member = service.findAllMembers().get(0);
        Address address = new Address();
        address.setFormattedAddress("Empire State Building, New York");
        address.setLat(200.033);
        address.setLng(36.665);
        service.updateMemberAddress(member, address);
    }
}

残念ながら、次のように恐ろしい StaleObjectStateException が発生します。

org.springframework.orm.jpa.JpaOptimisticLockingFailureException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.sose.domain.Address#1]; 

サンプル github アプリを使用して問題を再現するには、次のものが必要です。

  • メイヴン
  • ギット
  • JDK6
  • MySQL

次の手順に従って、問題を再現できます。

  • git clone git@github.com:balteo/StaleObjectStateException.git
  • mysql で、sose というデータベース スキーマを作成します。create database sose;
  • mvn test
  • そして出来上がり:BOOM

私の場合にこの例外が発生する理由と、この例外を取得せずに住所インスタンスを更新する方法を誰かに説明してもらえますか?

4

1 に答える 1

3

あなたのコードには 2 つの問題があります。

1) メンバーのアドレスを設定するときは、id を変更するのではなく、アドレス オブジェクトを設定する必要があります。updateMemberAddressこのようにする必要があります

@Override
public void updateMemberAddress(Member member, Address address) {
  member.setAddress(address);
  updateMember(member);
}

2) メンバーとアドレスが 1 対 1 の関係にある。あなたの正確なデータモデルはわかりませんが、私が見る限り、メンバーにはIDがあり、アドレスには明示的に定義されたIDがありません. これは必須ではありません。なぜなら、アドレスにはメンバーの ID があり、同時にそれが主キーでもあるからです (1:1 の関係)。

しかし、アドレス (ステートメントaddress.setId(addressId);) の id を変更すると、その時点で、同じ主キーを持つ 2 つのアドレス オブジェクト (以前はメンバーに関連付けられていた古いアドレスと新しいアドレス) ができます。Hibernate は、同じ主キーを持つ 2 つのインスタンスを処理できません。

新しいアドレス インスタンスをこのメンバーにアタッチする前に、古いアドレス インスタンスを削除する必要があります。(より良い解決策は、アドレスにバージョン番号または別の ID を追加し、関係 member:address を 1:n に変更することです。)

おそらく問題 2) でエラーが発生します。

于 2013-04-11T13:14:36.197 に答える