2013-04-03 35 views
5

Springコントローラメソッドに問題があります。実際には同じエンティティ上で2つの更新が行われ、StaleObjectStateExceptionが発生します。Hibernate StaleObjectStateExceptionが発生しないようにする

問題があるため、Memberインスタンスを取得すると、広告インスタンス(これは実際には必要ない)の更新(//UPDATE ONE参照)が発生し、Advertisementインスタンスを更新すると(//UPDATE TWO参照) StaleObjectStateExceptionをスローします。

私のケースでは、この例外が発生するのを防ぐにはどうすればよいですか(Spring Data JPAを使用していますか?ここ

Memberエンティティクラスである:

@Entity 
public class Member { 
... 
@OneToMany(fetch = FetchType.LAZY, mappedBy = "member") 
private List<Advertisement> advertisements; 
... 

Advertisementエンティティ・クラス内の:

@NotNull 
@ManyToOne(fetch = FetchType.LAZY) 
private Member member; 

ここ制御方法であって:

@RequestMapping(value = "/family/advertisement/edit", method = RequestMethod.POST, produces = "text/html") 
    public String editFamilyAdvertisement(@ModelAttribute @Validated(value = Validation.AdvertisementCreation.class) FamilyAdvertisementInfo familyAdvertisementInfo,   BindingResult bindingResult, Model model) { 
     Member member = memberService.retrieveCurrentMember();//UPDATE ONE 
     if (!advertisementService.advertisementBelongsToMember(familyAdvertisementInfo.getFamilyAdvertisement(), member)) { 
      throw new IllegalStateException("advertisement does not belong to member"); 
     } 
     if (bindingResult.hasErrors()) { 
      populateModel(model, familyAdvertisementInfo); 
      return "family/advertisement/edit"; 
     } 
     familyAdvertisementInfo.getFamilyAdvertisement().setMember(member); 
     advertisementService.editFamilyAdvertisement(familyAdvertisementInfo.getFamilyAdvertisement());//UPDATE TWO 
     return "redirect:/family/advertisement/edit/" + familyAdvertisementInfo.getFamilyAdvertisement().getId(); 
    } 

ここでは、スタックトレースです。

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.bignibou.domain.FamilyAdvertisement#1] 
    org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:303) 
    org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:151) 
    org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76) 
    org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:903) 
    org.hibernate.internal.SessionImpl.merge(SessionImpl.java:887) 
    org.hibernate.internal.SessionImpl.merge(SessionImpl.java:891) 
    org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:879) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:601) 
    org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366) 
    com.sun.proxy.$Proxy45.merge(Unknown Source) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:601) 
    org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241) 
    com.sun.proxy.$Proxy44.merge(Unknown Source) 
    org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:345) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    java.lang.reflect.Method.invoke(Method.java:601) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:91) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 
    com.sun.proxy.$Proxy52.save(Unknown Source) 
    com.bignibou.service.AdvertisementServiceImpl_Roo_Service.ajc$interMethod$com_bignibou_service_AdvertisementServiceImpl_Roo_Service$com_bignibou_service_AdvertisementServiceImpl$updateFamilyAdvertisement(AdvertisementServiceImpl_Roo_Service.aj:58) 
    com.bignibou.service.AdvertisementServiceImpl.updateFamilyAdvertisement(AdvertisementServiceImpl.java:1) 
    com.bignibou.service.AdvertisementServiceImpl_Roo_Service.ajc$interMethodDispatch1$com_bignibou_service_AdvertisementServiceImpl_Roo_Service$com_bignibou_service_AdvertisementServiceImpl$updateFamilyAdvertisement(AdvertisementServiceImpl_Roo_Service.aj) 
    com.bignibou.service.AdvertisementServiceImpl.editFamilyAdvertisement(AdvertisementServiceImpl.java:27) 
    com.bignibou.controller.AdvertisementController.editFamilyAdvertisement(AdvertisementController.java:85) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 

編集1

SQLログ:

2013-04-06 11:23:24,339 [http-bio-8080-exec-9] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Returning handler method [public java.lang.String com.bignibou.controller.AdvertisementController.editFamilyAdvertisement(com.bignibou.controller.helpers.FamilyAdvertisementInfo,org.springframework.validation.BindingResult,org.springframework.ui.Model)] 
Hibernate: 
    /* load com.bignibou.domain.GeolocationPostcode */ select 
     geolocatio0_.id as id8_0_, 
     geolocatio0_.postcode as postcode8_0_, 
     geolocatio0_.version as version8_0_ 
    from 
     geolocation_postcode geolocatio0_ 
    where 
     geolocatio0_.id=? 
Hibernate: 
    /* load com.bignibou.domain.Member */ select 
     member0_.id as id6_1_, 
     member0_.activated as activated6_1_, 
     member0_.address as address6_1_, 
     member0_.email as email6_1_, 
     member0_.last_connection_date as last4_6_1_, 
     member0_.password as password6_1_, 
     member0_.registration_date as registra6_6_1_, 
     member0_.role as role6_1_, 
     member0_.token as token6_1_, 
     member0_.version as version6_1_, 
     address1_.id as id3_0_, 
     address1_.formatted_address as formatted2_3_0_, 
     address1_.latitude as latitude3_0_, 
     address1_.longitude as longitude3_0_, 
     address1_.version as version3_0_ 
    from 
     member member0_ 
    left outer join 
     address address1_ 
      on member0_.address=address1_.id 
    where 
     member0_.id=? 
Hibernate: 
    /* load one-to-many com.bignibou.domain.Member.advertisements */ select 
     advertisem0_.member as member6_1_, 
     advertisem0_.id as id0_1_, 
     advertisem0_.id as id0_0_, 
     advertisem0_.active as active0_0_, 
     advertisem0_.creation_date as creation3_0_0_, 
     advertisem0_.description as descript4_0_0_, 
     advertisem0_.expiration_date as expiration5_0_0_, 
     advertisem0_.member as member0_0_, 
     advertisem0_.validated as validated0_0_, 
     advertisem0_.version as version0_0_, 
     advertisem0_.childminder_status as childmin1_10_0_, 
     advertisem0_.clazz_ as clazz_0_ 
    from 
     (select 
      id, 
      active, 
      creation_date, 
      description, 
      expiration_date, 
      validated, 
      version, 
      member, 
      null as childminder_status, 
      1 as clazz_ 
     from 
      family_advertisement 
     union 
     select 
      id, 
      active, 
      creation_date, 
      description, 
      expiration_date, 
      validated, 
      version, 
      member, 
      childminder_status, 
      2 as clazz_ 
     from 
      childminder_advertisement 
    ) advertisem0_ 
where 
    advertisem0_.member=? 

広告:

@RooJavaBean 
@RooToString 
@RooEquals 
@RooJpaEntity(inheritanceType = "TABLE_PER_CLASS") 
@Entity 
@DynamicUpdate 
public abstract class Advertisement { 

    @ElementCollection 
    private Set<ChildcareType> childcareTypes; 

    @ManyToMany 
    private List<DayToTimeSlot> dayToTimeSlots; 

    @NotNull(groups = { Validation.AdvertisementCreation.class }) 
    @ManyToMany 
    private Set<GeolocationPostcode> postcodes; 

    @NotNull(groups = { Default.class }) 
    @Size(min = 6, max = 300, groups = { Default.class, Validation.AdvertisementCreation.class }) 
    @Column(length = 300) 
    private String description; 

    @Temporal(TemporalType.TIMESTAMP) 
    @DateTimeFormat(pattern = "dd/MM/yyyy HH:mm:ss") 
    private Date creationDate; 

    @Temporal(TemporalType.TIMESTAMP) 
    @DateTimeFormat(pattern = "dd/MM/yyyy HH:mm:ss") 
    private Date expirationDate; 

    @NotNull(groups = { Default.class }) 
    private boolean active; 

    @NotNull(groups = { Default.class }) 
    private boolean validated; 

    @NotNull 
    @ManyToOne(fetch = FetchType.LAZY) 
    private Member member; 

    public abstract boolean isChildcareTypesValid(); 

    @AssertTrue(groups = { Validation.AdvertisementCreation.class }) 
    public boolean isPostcodesValid() { 
     return (postcodes != null && !postcodes.isEmpty()); 
    } 

} 

FamilyAdvertisement:

@RooJavaBean 
@RooToString 
@RooEquals 
@RooJpaEntity 
@Entity 
@DynamicUpdate 
public class FamilyAdvertisement extends Advertisement { 

    @NotNull(groups = Validation.AdvertisementCreation.class) 
    @ElementCollection 
    private Set<Need> needs; 

    @ElementCollection 
    private Set<ChildminderStatus> childminderStatuses; 

    @AssertTrue 
    @Override 
    public boolean isChildcareTypesValid() { 
     return true; 
    } 

    @AssertTrue 
    public boolean isNeedsValid() { 
     if (needs.isEmpty()) { 
      return false; 
     } 
     if (needs.contains(Need.CHILDMINDER_TO_FAMILY)) { 
      return false; 
     } 
     return true; 
    } 

} 

edit 2:私はそのことを言及しなければならない私はSpring Roo + JPAによってデフォルトで提供されるエンティティのバージョニングを使用します。私はこれを考慮に入れなければならないと確信しています。

編集3:githubでhereの問題を示すサンプルアプリケーションをセットアップしました。私を助けたい人は、次のようにアプリをクローンできます:git clone https://github.com/balteo/sample-app-gab

+0

[Hibernate Profiler](http://www.hibernatingrhinos.com/products/hprof)で何をしているのか見てみましたか?あなたのセッションスコープで何かが起こっているようです。 –

+0

ありがとうMootinator。セッションのスコープに何か問題があったと思われる具体的な行は何ですか? – balteo

+0

特有のものではなく、ただの匂い。あなたのケースを考えれば、マッピングの問題よりも明らかに可能性は低いです。 –

答えて

3

"行が別のトランザクションによって更新または削除されました(または保存されていない値のマッピングが間違っていました):[com.bignibou.domain.FamilyAdvertisement#1]"は問題の2つの理由を示しますJPAマッピングでのエラー(あなたがデータを選択している場合は異常です)またはエラーです。

JPAによって生成されたSQL照会ロギングとポストSQL照会を追加してください。 FamilyAdvertismentクラスも投稿してください。クエリログはJPAプロバイダによって異なります(ヒントについては、これを参照してください:How to view the SQL queries issued by JPA?

+0

こんにちはPiotr!私は要求されたように自分の投稿を編集しました。 – balteo

+0

こんにちはPiotr。 JPAのマッピングで_errorの種類を詳しく教えてください。 – balteo

+0

こんにちはPiotr、私は私の投稿を編集し、問題を示すgithubのサンプルアプリケーションを含めました。 – balteo

9

私は同様の問題がありました。これは、実行時に発生するエラーです。mvn test

org.hibernateです。StaleObjectStateException:行が

は、それが原因で、誤って、私はNULLとしての実体のversionフィールドを初期化して更新または削除別のトランザクションによって(または保存されていないと値のマッピングが間違っていました)した

一度
mysql> select * from lap; 
+----+-------------+---------------------+------+------------------+---------+---------+ 
| id | code  | cumulative_distance | name | partial_distance | version | race_id | 
+----+-------------+---------------------+------+------------------+---------+---------+ 
| 1 | AF0CUL00011 |    1000 | LAP1 |    1000 | NULL |  1 | 
| 2 | AQ000000012 |    2000 | LAP2 |    1000 | NULL |  2 | 
| 3 | AR000000013 |    3000 | LAP3 |    1000 | NULL |  3 | 
| 4 | AR0000N0114 |    4000 | LAP4 |    1000 | NULL |  4 | 
+----+-------------+---------------------+------+------------------+---------+---------+ 
4 rows in set (0.00 sec) 

、私は0でversionフィールドを初期化、すべてが働いていた:私はそれが役に立てば幸い

mysql> select * from lap; 
+----+-------------+---------------------+------+------------------+---------+---------+ 
| id | code  | cumulative_distance | name | partial_distance | version | race_id | 
+----+-------------+---------------------+------+------------------+---------+---------+ 
| 1 | AF0CUL00011 |    1000 | LAP1 |    1000 |  0 |  1 | 
| 2 | AQ000000012 |    2000 | LAP2 |    1000 |  0 |  2 | 
| 3 | AR000000013 |    3000 | LAP3 |    1000 |  0 |  3 | 
| 4 | AR0000N0114 |    4000 | LAP4 |    1000 |  0 |  4 | 
+----+-------------+---------------------+------+------------------+---------+---------+ 
4 rows in set (0.00 sec) 

、 アグスティン

関連する問題