首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果重新初始化集合,则Hibernate将触发delete语句

如果重新初始化集合,则Hibernate将触发delete语句
EN

Stack Overflow用户
提问于 2021-09-10 17:40:27
回答 1查看 43关注 0票数 1

我有一个有线问题,Hibernate在更新实体时触发了一些删除查询。

实体:

代码语言:javascript
复制
public class Item {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "item_tags", joinColumns = @JoinColumn(name = "item_id"), inverseJoinColumns = @JoinColumn(name = "tag_id"))
    private Set<Tag> tags = new HashSet<>();

    // some more stuff
}

更新实体的代码:

代码语言:javascript
复制
public Item updateItem(ItemUpdateRequest dto, long id) {
    Item item = itemDao.findEntity(id);
    item = ItemMapper.INSTANCE.mapFromUpdateRequest(item, dto, tagDao);

    // save item so it can be returned
    item = itemDao.saveEntity(item);
    return item;
}

由于请求只发送标签的ID,因此MapStruct使用自定义方法生成ItemMapper来加载标签:

代码语言:javascript
复制
@AfterMapping
default void loadTags(@MappingTarget Item item, ItemUpdateRequest dto, @Context TagDAO dao) {
    Collection<Tag> tags = dao.findEntities(dto.getTags());
    item.setTags(new HashSet<>(tags));
}

因此,生成的MapStruct映射器如下所示:

代码语言:javascript
复制
@Override
public Item mapFromUpdateRequest(Item item, ItemUpdateRequest dto, TagDAO tagDao) {
    if ( dto == null ) {
        return null;
    }

    item.setDescription( dto.getDescription() );
    item.setPrice( dto.getPrice() );
    item.setTitle( dto.getTitle() );
    item.setType( itemTypeMapper.map( dto.getType() ) );

    loadTags( item, dto, tagDao );

    return item;
} 

如果ItemUpdateRequest不包含标记,Hibernate将执行以下查询:

代码语言:javascript
复制
1. Hibernate: update public.item set description=?, price=?,
restaurant_id=?, title=?, type=?, visible=? where id=?
2. Hibernate: select tags0_.item_id as item_id1_20_0_, tags0_.tag_id as
tag_id2_20_0_, tag1_.id as id1_41_1_, tag1_.abbreviation as
abbrevia2_41_1_, tag1_.text as text3_41_1_ from public.item_tags
tags0_ inner join public.tag tag1_ on tags0_.tag_id=tag1_.id where
tags0_.item_id=?
3. Hibernate: delete from public.item_tags where item_id=?

我不明白为什么Hibernate会发出删除查询。DB和ItemUpdateRequest中的项目都不包含任何标记。我认为Hibernate可能会识别更改后的集合,因为它是在loadTags()方法中再次设置的。

我尝试编写单元测试,但此测试按预期工作,并且没有发出任何delete语句。

代码语言:javascript
复制
@Test
public void deleteQueryTest() {
    Item item = Item.builder().title("Test").build();
    item = itemDao.saveEntity(item);

    Item reloadedItem = itemDao.findEntity(item.getId());
    ItemUpdateRequest updateRequest = ItemUpdateRequest.builder()
        .title("Test Changed")
        .build();
    reloadedItem = ItemMapper.INSTANCE.mapFromUpdateRequest(reloadedItem, updateRequest, tagDao);
    reloadedItem = itemDao.saveEntity(reloadedItem);
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-13 14:12:27

loadTags方法始终设置新的集合实例,而不是更改支持脏跟踪的现有集合实例。请尝试使用item.getTags().clear(); item.getTags().addAll(tags);。除此之外,我建议您研究一下Blaze-Persistence Entity Views,它可以通过执行更少的查询来进一步提高事务性能。

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间轻松映射,就像Spring数据在类固醇上的投影一样。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(Getter)映射到实体模型。

使用Blaze-Persistence实体视图,您的用例的DTO模型可能如下所示:

代码语言:javascript
复制
@EntityView(Item.class)
@UpdatableEntityView
public interface ItemDto {
    @IdMapping
    Long getId();
    String getTitle();
    void setTitle(String title);
    String getDescription();
    void setDescription(String description);
    BigDecimal getPrice();
    void setPrice(BigDecimal price);
    ItemTypeDto getType();
    void setType(ItemTypeDto type);
    Set<TagDto> getTags();

    @EntityView(ItemType.class)
    interface ItemTypeDto {
        @IdMapping
        Long getId();
    }
    @EntityView(Tag.class)
    @UpdatableEntityView
    @CreatableEntityView
    interface TagDto {
        @IdMapping
        Long getId();
        String getAbbreviation();
        void setAbbreviation(String abbreviation);
        String getText();
        void setText(String text);
    }
}

查询就是将实体视图应用于查询,最简单的就是通过id进行查询。

ItemDto a = entityViewManager.find(entityManager, ItemDto.class, id);

Spring数据集成允许您像使用Spring数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

代码语言:javascript
复制
Page<ItemDto> findAll(Pageable pageable);

最好的部分是,它将只获取实际需要的状态!

多亏了Jackson和Spring MVC的集成,你的代码看起来就像这样简单:

代码语言:javascript
复制
public interface ItemDtoRepository extends EntityViewRepository<ItemDto, Long>  {

}
代码语言:javascript
复制
    @Autowired
    private ItemDtoRepository itemRepository;

    @RequestMapping(path = "/items/{id}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<String> updateItem(@EntityViewId("id") @RequestBody ItemDto itemDto) {
        itemRepository.save(itemDto);
        return ResponseEntity.ok(itemDto.getId().toString());
    }

有关更多详细信息,还可以查看文档:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#entity-view-deserialization

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69136057

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档