我有两节课,比如说
@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。
提前致谢。
答案 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);
}
如果名称或提供者引用已更改,将仅发布更新语句。