【问题标题】:Understanding the optimistic locking StaleObjectStateException了解乐观锁定 StaleObjectStateException
【发布时间】:2013-04-02 15:03:32
【问题描述】:

我已经与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 类中的成员地址,如下所示:

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

  • 马文
  • 吉特
  • JDK 6
  • MySQL

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

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

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

【问题讨论】:

    标签: hibernate jpa spring-data-jpa


    【解决方案1】:

    我在您的代码中发现了两个问题:

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

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

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

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

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

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

    【讨论】:

    • 嗨,约翰娜!嗯...地址还定义了一个 id 字段。它实际上位于一个单独的 aspectj 文件中(与 Member 一样)。如果我不设置地址的 id 会发生什么,我最终会在地址表中得到另一个地址实例...
    • 如果您设置了地址 ID,那么您在数据库中只有一个实例,因为第二个实例由于异常而未写入;-)。即使在地址中使用单独的地址 id 以您这样做的方式设置 id 也是无稽之谈。然后,您还有两个具有相同主键的实例。在您的声明中Address address = new Address();您明确创建了第二个地址实例,这需要一个唯一的 id。如果你不想要第二个地址实例,你必须这样做:Address address = member.getAddress(); address.setXXX(...); service.updateMember(member);
    • 使用这个:Address address = member.getAddress(); 然后我必须一个一个地管理/复制字段,如果有很多字段,这可能会很乏味。实际上我想避免这种情况。
    • 在 Hibernate 中,Java 对象的一个​​实例是一个数据库行。如果要更新数据库行,则必须修改此 Java 实例。如果要插入新行,则必须在 Java 中创建一个新实例。如果您两次加载同一行,则两次都会返回相同的实例。你不能绕过这种行为。 (只要您使用一个 Session 实例,所有这些都是这种情况。使用更多 Session 实例会变得更加复杂,但您无法避免这些限制。)
    猜你喜欢
    • 1970-01-01
    • 2010-09-12
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多