【问题标题】:Update entity from DTO with hibernate erases other fields使用休眠从 DTO 更新实体会擦除其他字段
【发布时间】:2014-06-11 12:41:21
【问题描述】:

假设我有两个班级

@Entity
public class Product{
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ManyToOne(fetch = FetchType.LAZY)
    private Provider provider;
    //getter & setter
}

@Entity
public class Provider{
    @Id
    @GeneratedValue
    private Long id;
    private String name
    //getter & setter
}

以及对应的DTO

public class ProductDTO{
    private Long id;
    private String name;
    private ProviderDTO provider;
    //getter & setter
}

为了简化 ProductDTO 和 product 具有相同的属性,我使用 dozer Mapping 将实体映射到 DTO 并将其发送到视图。我有一个视图,用户只能在 productDTO.name 中进行更改(这是一个更新),当他将更改发送到服务器时,我会做出类似

ProductServices.update(productDTO);

ProductServices 有

@Transactional
public void update(ProductDTO p){
        Product p = DozerMapper.map(productDTO,ProductDTO.class);
        productDao.update(p);
}

productDao 内部(我使用的是spring,所以我正在注入sessionFactory 并在做,ProductServices 有@Transactioanl 注释)

this.getCurrentSession().update(p);

所以 Hibernate 进行更新并设置 provider_id=null,我知道这是正确的,因为我发送到 ProductServices.update 的产品没有设置提供程序,Hibernate 将使用该产品没有提供者在数据库中更新它。 问题是我不确定如何解决这个问题,我是 hibernate 的新手,我发现的唯一解决方案是逐个字段执行选择和检查,看看什么值发生了变化,但是如果,例如,我有一个更复杂的对象,我将不得不编写大量代码来检查每个字段。

有人知道更好的解决方案或我如何修改 mi 代码以正确处理这种情况?

我正在使用 Spring 3.1 和休眠 3.6。

提前致谢。

【问题讨论】:

  • ProductDTO@EntityProvider 不是? -- 这是故意的还是错误的?
  • 改了,是版本错误

标签: java spring hibernate jpa dto


【解决方案1】:

问题不是休眠,“问题”是推土机。

您需要先从数据库加载实体(休眠)。然后让Dozer只修改你要修改的字段(而不是id),然后让Hibernate保存更新的实体。

我不详细了解Dozer,所以把这段代码当作一个原始草图:

@Transactional
public void update(ProductDTO dto){
    Product p = productDao.loadById(dto.getId());
    DozerMapper.map(dto,p); //you need to configure dozer to that it only map the fields you want to map.
    productDao.update(p);
}

【讨论】:

  • 感谢@Ralph,但如果我在每次更新之前都选择 (loadById) 会影响性能?
  • @UserMan:你需要这样做——没有别的办法。但性能影响不会那么大。顺便说一句:几乎每个基于 Hibernate 或 JPA 的 Web 应用程序都以这种方式处理它。
  • 谢谢,只是最后一个问题,如果我删除 DTO 并直接使用实体,你怎么看?我使用 OpenSessionInViewFilter 来处理惰性对象,但我不确定该过滤器是否良好。
  • @UserMan:这是个坏主意,因为恶意用户可能会向服务器发送经过操纵的更新 http-request 以更新实体中的每个字段。
  • 如果我有带有 id 的嵌套对象怎么办?有没有办法不为每个嵌套实体调用 loadById?某种级联?
【解决方案2】:

这就是Blaze-Persistence Updatable Entity Views 的用武之地,它完全支持这个用例。它在幕后使用脏跟踪来了解发生了什么变化,并且在刷新期间只会更新这些属性。使用实体视图将在许多方面提高您的性能,并允许您删除大量样板代码。

您将看到的第一个重大改进是,当实体视图应用于查询时,它将自动只获取真正需要的数据,而不是进行延迟加载或要求您手动进行获取连接。

下一个改进是可更新的实体视图将减少进行更新所需的查询量。它可以根据脏跟踪信息进行更新,从而避免在执行DML语句之前加载数据。

您的用例的可更新实体视图可能如下所示

@EntityView(Product.class)
@UpdatableEntityView
public interface ProductDTO {
    Long getId();
    String getName();
    void setName(String name);
    ProviderDTO getProvider();
    void setProvider(ProviderDTO provider);
}

@EntityView(Provider.class)
public interface ProviderDTO {
    Long getId();
    String getName();
}

这将使产品的名称和提供者参考可更新。你可以像这样更新它

@Transactional
public void update(ProductDTO p) {
        entityViewManager.update(entityManager, p);
}

如果名称或提供者引用发生更改,它将仅发出更新语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-16
    • 2019-06-22
    • 2016-12-20
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 1970-01-01
    相关资源
    最近更新 更多