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
.
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. –
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
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. –