2013-04-03 30 views
5

Tôi gặp sự cố với phương thức bộ điều khiển Spring. Nó thực sự thực hiện hai cập nhật trên cùng một thực thể gây ra StaleObjectStateException.Ngăn chặn StaleObjectStateException hibernate xuất hiện

Vấn đề là khi tôi truy xuất cá thể thành viên, tôi nghĩ nó bằng cách nào đó làm cho bản cập nhật (xem //UPDATE ONE) của trường hợp Quảng cáo (điều này không thực sự muốn) và khi tôi cập nhật trường hợp Quảng cáo (xem //UPDATE TWO), nó ném một số StaleObjectStateException.

Câu hỏi của tôi là làm thế nào tôi có thể ngăn chặn ngoại lệ này xảy ra trong trường hợp của tôi (lưu ý tôi sử dụng dữ liệu Spring JPA)?

Đây là lớp Member thực thể:

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

và trong lớp Advertisement thực thể:

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

Dưới đây là phương pháp điều khiển:

@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(); 
    } 

Đây là stacktrace:

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) 

chỉnh sửa 1:

logs 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=? 

Quảng cáo:

@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; 
    } 

} 

chỉnh sửa 2: Tôi phải đề cập rằng Tôi sử dụng phiên bản thực thể như được cung cấp theo mặc định bởi Spring Roo + JPA. Tôi khá chắc chắn điều này phải được đưa vào tài khoản.

chỉnh sửa 3: Tôi đã thiết lập ứng dụng mẫu thể hiện sự cố here trên github. Bất cứ ai muốn giúp tôi có thể sao chép các ứng dụng như sau: git clone https://github.com/balteo/sample-app-gab.

+0

Bạn đã thử nhìn vào những gì nó đang làm với [Hibernate Profiler] (http://www.hibernatingrhinos.com/products/hprof) gần như có vẻ như có điều gì đó đang xảy ra với phạm vi phiên của bạn. –

+0

Cảm ơn Mootinator. Dòng cụ thể nào khiến bạn nghĩ rằng có điều gì đó sai trái với phạm vi phiên? – balteo

+0

Không có gì cụ thể, chỉ là một mùi. Chắc chắn ít có khả năng hơn một vấn đề lập bản đồ, cho trường hợp của bạn. –

Trả lời

3

"org.hibernate.StaleObjectStateException: Hàng đã được cập nhật hoặc xóa bởi một giao dịch khác (hoặc ánh xạ giá trị chưa được lưu không chính xác): [com.bignibou.domain.FamilyAdvertisement # 1]" đề xuất hai lý do cho sự cố - một số bất ngờ cập nhật (sẽ là lạ khi bạn đang chọn dữ liệu) hoặc lỗi trong ánh xạ JPA.

Vui lòng thêm đăng nhập truy vấn SQL và đăng truy vấn SQL được tạo bởi JPA. Vui lòng đăng lớp FamilyAdvertisment. Nhật ký truy vấn phụ thuộc vào nhà cung cấp JPA (xem gợi ý này: How to view the SQL queries issued by JPA?)

+0

Xin chào Piotr! Tôi đã chỉnh sửa bài đăng của mình theo yêu cầu. – balteo

+0

Xin chào lần nữa Piotr. Bạn có thể vui lòng giải thích về loại _error trong JPA mapping_ mà bạn đang đề cập đến không? – balteo

+0

Xin chào Piotr, tôi đã chỉnh sửa bài đăng của mình và đưa vào một ứng dụng mẫu trên github thể hiện sự cố. – balteo

9

Tôi cũng gặp vấn đề tương tự. Đây là lỗi mà tôi nhận được khi chạy mvn test:

org.hibernate.StaleObjectStateException: Row đã được cập nhật hoặc xóa bởi giao dịch khác (hoặc bản đồ chưa được lưu giá trị không chính xác)

Nó được bởi vì, do nhầm lẫn, tôi khởi sự version lĩnh vực của thực thể như NULL:

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) 

Một lần, tôi khởi lĩnh vực version với 0, tất cả mọi thứ làm việc:

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) 

tôi hy vọng nó sẽ giúp, Agustin

Các vấn đề liên quan