【问题标题】:JPA/hibernate is generating the wrong SQL statementJPA/hibernate 正在生成错误的 SQL 语句
【发布时间】:2018-10-10 13:00:12
【问题描述】:

我的JpaRepository 生成了错误的 SQL。它只更新商店而不是实体的其余部分。仔细观察后,我注意到查询说 update shop set 没有提到 campaign 内部有 shop 的实体。

Campaign 内有一家商店:

 @JoinColumn(name = "SHOP_ID", referencedColumnName = "SHOP_ID", nullable = false)
    @ManyToOne(cascade = CascadeType.ALL)
    private Shop shop;

在店里我有一套:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "shop")
private Set<Campaign> campaignSet;

使用saveAndFlush 生成的查询 -

update shop set created_at=?, currency=?, currency_iso_code=?, default_delivery_cost=?, delivery_cost_erp_number=?, description=?, default_language_id=?, modified_at=?, sales_organisation_id=?, code=?, valid_from=?, valid_till=? where shop_id=?

我希望保存整个广告系列,老实说,我并不关心更新商店,因为在更新广告系列时我永远不会这样做。

方法更新:

@Override
@Modifying
@Transactional
public CampaignDto update(CampaignDto campaignDto) throws RequestNotFoundException {
    //TODO: compare objects to see if there is a change

    Campaign campaign = mapper.mapReverse(campaignDto);

    if (campaign.getCampaignId() != null) {
        campaign = campaignRepository.getOne(campaign.getCampaignId());

        if (campaign.getCampaignId() == null) {
            throw new RequestNotFoundException(
                    String.format("Campaign %s is not found", campaign.getKey().toString()));
        }
    }

    Shop shop = shopRepository.getOne(campaignDto.getShopId());

    if (shop.getShopId() > 0 && shop.getShopId() != null) {
        shop.setCode(campaignDto.getShopCode());
        shop.setCurrency(campaignDto.getShopCurrency());
        shop.setCurrencyIsoCode(campaignDto.getShopCurrencyIso());
        shop.setValidFrom(campaignDto.getValidFrom());
        shop.setValidTill(campaignDto.getValidTill());
        shop.setCreatedAt(OffsetDateTime.now());
        shop.setSalesOrganisationId(campaignDto.getSalesOrganisationId());
    } else {
        throw new IllegalArgumentException("Must declare an existing shop to update a campaign.");
    }

    Language language = languageRepository.getOne(campaignDto.getLanguageId());
    if (!StringUtils.isEmpty(language.getLanguageId()) && language.getLanguageId() != null) {
        language.setLanguageId(campaignDto.getLanguageId());
        shop.setLanguage(language);
    } else {
        throw new IllegalArgumentException("Must declare an existing language to update a campaign.");
    }

    campaign.setShop(shop);
    CampaignDto updatedCampaign = mapper.map(campaign);
    campaignRepository.saveAndFlush(campaign);

    return updatedCampaign;
}

【问题讨论】:

    标签: spring-boot jpa spring-data spring-data-jpa jpa-2.1


    【解决方案1】:

    老实说,我并不关心更新商店,因为在更新广告系列时我永远不会这样做

    那为什么是CascadeType.ALL?您永远不应该永远CascadeType.ALL 与多对一关联一起使用。这是因为CascadeType.MERGE 很少有意义(你基本上是在乞求像你描述的那样的意外副作用),而CascadeType.REMOVE 只是纯粹的邪恶(通过移除孩子,你移除了父母,这反过来会导致所有它的其他孩子要被删除,因为你在Shop.campaigns 上有另一个CascadeType.ALL)。

    另外,如果您不想在保存Campaign 的同时更新Shop,那么以下代码的目的是什么?

    if (shop.getShopId() > 0 && shop.getShopId() != null) {
        shop.setCode(campaignDto.getShopCode());
        shop.setCurrency(campaignDto.getShopCurrency());
        shop.setCurrencyIsoCode(campaignDto.getShopCurrencyIso());
        shop.setValidFrom(campaignDto.getValidFrom());
        shop.setValidTill(campaignDto.getValidTill());
        shop.setCreatedAt(OffsetDateTime.now());
        shop.setSalesOrganisationId(campaignDto.getSalesOrganisationId());
    }
    

    如果您真的不想更新Shop,只需删除该部分即可。附带说明一下,您可能希望交换 if 子句中的条件。

    最后,如果您没有看到CAMPAIGN 表的更新,可能是因为Campaign 实体没有实际更改。一般来说,Hibernate 将检索当前状态并将其与更新的Entity 进行比较,以避免不必要的UPDATE 语句。

    【讨论】:

    • 首先,从Campaign.shop 中删除CascadeType.ALL,您将不会看到对SHOP 表的更新。其次,您能否验证mapper.mapReverse(campaignDto) 确实返回了Campaign 的实例,其值与数据库中已有的值不同?
    • 另外,正如我所说,Hibernate 必须首先检索 Campaign 的当前状态作为合并过程的一部分,因此在调用 campaignRepository.saveAndFlush(campaign) 时看到 SELECT 语句是可以预料的跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-14
    • 2022-12-02
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多