理解乐观锁定StaleObjectStateException

时间:2013-04-10 16:32:45

标签: hibernate jpa spring-data-jpa

我一直在与StaleObjectStateException争吵超过一周,并决定在这里发布一个简单的应用程序来重现问题。

我了解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 
}

使用新鲜/非托管地址实例托管成员实例,我正在尝试更新ServiceImpl类中的成员地址,如下所示:< / p>

@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应用程序重现问题的人都需要:

  • 的Maven
  • GIT中
  • JDK 6
  • MySQL的

他们可以按照以下步骤重现问题:

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

任何人都可以向我解释为什么在我的情况下发生此异常以及如何更新地址实例而不会出现此异常?

1 个答案:

答案 0 :(得分:3)

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

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

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

2)您在成员和地址之间存在一对一的关系。我不知道你的确切数据模型,但据我所知,成员有一个id和地址没有明确定义的id。这不是必需的,因为地址具有成员的id,在同一时刻它是主键(1:1关系)。

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

在将新地址实例附加到此成员之前,您必须删除旧地址实例。 (更好的解决方案是添加版本号或单独的ID来解决并将关系成员:地址更改为1:n。)

可能问题2)产生错误。