首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解乐观锁定StaleObjectStateException

理解乐观锁定StaleObjectStateException
EN

Stack Overflow用户
提问于 2013-04-10 16:32:44
回答 1查看 10.3K关注 0票数 3

我已经和StaleObjectStateException做了一个多星期的斗争,并且决定在这里发布一个简单的应用程序来重现这个问题。

我理解org.hibernate.StaleObjectStateException是一个乐观锁定异常。此外,乐观锁定依赖于在每个实体类中使用版本字段

现在让我解释一下我是如何复制上述异常的:示例应用程序有一个Member实体类,如下所示:

代码语言:javascript
复制
@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实体类:

代码语言:javascript
复制
@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 
}

使用新/非托管地址实例托管成员实例,我试图按以下方式更新ServiceImpl类中的成员地址:

代码语言:javascript
复制
@Override
public void updateMemberAddress(Member member, Address address) {
    long addressId = member.getAddress().getId();
    address.setId(addressId);
    updateAddress(address);
}

下面是测试类:

代码语言:javascript
复制
@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如下:

代码语言:javascript
复制
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应用程序再现问题的人都需要:

  • Maven
  • Git
  • JDK 6
  • MySQL

它们可以通过以下步骤再现问题:

  • git clone git@github.com:balteo/StaleObjectStateException.git
  • 在mysql中创建一个名为sose create database sose;的数据库模式
  • mvn test
  • 瞧:BOOM

请任何人向我解释为什么在我的情况下会出现这个异常,以及如何在没有得到这个异常的情况下更新地址实例?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-04-11 13:14:36

我在您的代码中看到了两个问题:

1)当设置成员的地址时,不应修改id,而应设置address对象。updateMemberAddress应该是这样的

代码语言:javascript
复制
@Override
public void updateMemberAddress(Member member, Address address) {
  member.setAddress(address);
  updateMember(member);
}

2)会员和地址之间有一对一的关系。我不知道您的确切数据模型,但据我所见,成员有一个id,而address没有显式定义的id。这是不必要的,因为address有成员的id,同时它是主键(1:1关系)。

但是,当您更改地址id (语句address.setId(addressId);)时,此时有两个地址对象(以前附加到成员的旧地址和新地址)具有相同的主键。Hibernate不能使用相同的主键处理两个实例。

在将新地址实例附加到该成员之前,必须删除旧地址实例。(一个更好的解决方案是添加版本号或单独的id,以寻址关系成员: address为1:n)。

可能是问题2)产生错误。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15931491

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档