2

当我试图从 REST 控制器中删除下面的 Customer 对象时,得到“删除分离的实例”异常。

日志:

org.springframework.dao.InvalidDataAccessApiUsageException: Removing a detached instance com.test.model.Customer#1750; nested exception is java.lang.IllegalArgumentException: Removing a detached instance com.test.model.Customer#1750

领域:

@Entity
public class Customer{

@Id
private Long id;

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;

// other stuff with getters/setters

}

休息控制器:

@Controller
@RequestMapping("/shop/services/customers")
public class CustomerRESTController {

   /**
     * Deletes a customer
     */
    @RequestMapping( value="/{id}", method=RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteCustomer(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {

        Customer customer = customerService.getById(id);
        if(customer != null){
            customerService.delete(customer);
        }else{
            response.sendError(503, "No Customer found for ID : " + id);
        }
    }

    // other stuff
}

我正在从数据库中获取客户对象,但仍然在休眠抱怨。有什么建议吗??

4

1 回答 1

3

实体在当前会话(或更好的事务)中分离。由于您在 Spring 中,因此使用Java 事务服务 (JTS)进行事务行为是很常见的。有了这样的 Hibernate 会在提交后自动清除持久性上下文(就像它在用作 JPA 解决方案时所做的那样)。

通常 Hibernate 不会清除会话的持久性上下文,因此您的实体通常不会在提交后分离。(这在分布式环境中是不安全的,但如果您仅使用 Hibernate 访问数据库并使用像 Ehcache 这样的分布式缓存,则可以保存)。

解决方案:session.merge(object)将您的实体重新附加到当前会话对象的持久化上下文。

它实际上不是合并,而是重新附加,如果 Hibernate 不确定实体的当前状态是否反映了正确的数据库缓存,它将重新加载实体。(并在存在版本属性(@Version)的情况下添加特殊行为)。

顺便说一下 Hibernate 的文档状态:

将给定对象的状态复制到具有相同标识符的持久对象上。如果当前没有与会话关联的持久实例,它将被加载。

更新

看到您的代码后,这看起来像是一个事务性问题。请检查您的 customerService.getById(id) 和 customerService.delete(customer) 服务调用是否导致事务提交。您需要将两者放在同一个事务中。

您还可以做的一件事也可以解决您的问题:

public void deleteCustomer(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {
    boolean wasDeleted = customerService.delete(id);
    if(!wasDeleted)
        response.sendError(503, "No Customer found for ID : " + id);
    }
}

这样您就不需要两次服务调用。在高级服务调用中使用休眠实体实际上是不典型的(但对于不同的架构可能会有所不同。我不太使用 Spring)。

于 2013-09-29T14:11:14.220 回答