使用hibernate从DTO更新实体会删除其他字段

时间:2014-06-11 12:41:21

标签: java spring hibernate jpa dto

我有两节课,比如说

@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和产品具有相同的属性,我使用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);
}

inside productDao(我正在使用spring,因此我正在注入sessionFactory并且正在执行,而ProductServices具有@Transactioanl注释)

this.getCurrentSession().update(p);

所以Hibernate进行更新并设置provider_id = null,我知道它是正确的,因为我发送给ProductServices.update的产品没有提供者集,而Hibernate将采用产品没有的产品提供者在数据库中更新它。 问题是我不知道如何解决这个问题,我是hibernate的新手,我发现的唯一解决方案是按字段执行选择和检查,看看哪些值已经改变,但是,例如,我有一个更复杂的对象,我将不得不编写大量代码来检查每个字段。

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

我正在使用Spring 3.1 with hibernate 3.6。

提前致谢。

2 个答案:

答案 0 :(得分:4)

问题不是休眠,问题"是推土机。

您需要先从数据库加载实体(Hibernate)。然后让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);
}

答案 1 :(得分:1)

这是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);
}

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