简体   繁体   中英

Generated Update sql for child entity is incorrect in One-to-many relation

I have two classes, ElwInfo and ElwUnderlyingAsset in parent-child relationship. Table names are ELW_INFO, ELW_UNDERLYING_ASSET and ELW_UNDERLYING_ASSET refers to ELW_INFO by column INSTRUMENT_CD.

I've mapped both classes in file 'Elw.hbm.xml' as follows

<class name="ElwInfo" table="ELW_INFO" >

    <id name="instrumentCode" column="INSTRUMENT_CD" ><generator class="assigned"/></id>
    <property name="instrumentName" column="INSTRUMENT_NM"/>
    <property name="ccyCode"        column="CCY_CD"/>

    <one-to-one name="instrumentCommon" 
                class="InstrumentCommon"
                cascade="save-update" lazy="false" />

    <one-to-one name="instrumentClass" 
                class="InstrumentClass"
                cascade="none" lazy="false" />

    <property name="issueDate"      column="ISSUE_DT"/>
    <property name="maturityDate"   column="MRTY_DT"/>
    <property name="convertRatio"   column="CONVERT_RATIO"/>
    <property name="strike"         column="STRIKE"/>
    <property name="koBarrier"      column="KO_BARRIER"/>
    <property name="minRebateRate"  column="MIN_REBATE_RT"/>
    <property name="addRebate"      column="ADD_REBATE"/>

    <many-to-one class="com.fistglobal.riskcraft.market.web.domain.instrument2.common.ExchangeInfo" 
                 name="exchangeInfo" column="EXCH_CD" lazy="false" cascade="none" 
                 unique="true" />
    <many-to-one class="com.fistglobal.riskcraft.market.web.domain.instrument2.common.ElwTypeInfo" 
                 name="elwType" column="ELW_TYPE_CD" lazy="false" cascade="none" 
                 unique="true" />
    <many-to-one class="com.fistglobal.riskcraft.market.web.domain.instrument2.common.OptionTypeInfo" 
                 name="optionType" column="OPTION_TYPE_CD" lazy="false" cascade="none" 
                 unique="true" />
    <many-to-one class="com.fistglobal.riskcraft.market.web.domain.instrument2.common.ExerciseTypeInfo" 
                 name="exerciseType" column="EXERCISE_TYPE_CD" lazy="false" cascade="none" 
                 unique="true" />

    **<bag name="underlyingAssetList" cascade="save-update" table="ELW_UNDERLYING_ASSET" lazy="false" >
        <key column="INSTRUMENT_CD"/>
        <one-to-many class="ElwUnderlyingAsset" not-found="ignore"/>
    </bag>**

</class>

<class name="ElwUnderlyingAsset" table="ELW_UNDERLYING_ASSET" >
    <composite-id name="id" class="ElwUnderlyingAsset$Id">
        <key-property name="instrumentCode" column="INSTRUMENT_CD"/>
        <key-property name="underlyingAssetCode" column="UNDERLYING_ASSET_CD"/>
        <generator class="assigned"/>
    </composite-id>

    <property name="qty"   column="UNDERLYING_ASSET_QTY"/>
</class>

<class name="ElwData" table="ELW_DATA">
    <composite-id name="key" class="InstrumentDataKey">
        <key-property name="dt"             column="DT"/>
        <key-property name="instrumentCode" column="INSTRUMENT_CD"/>
    </composite-id>
    <property name="barrierHitYn"   column="BARRIER_HIT_YN"/>
    <property name="barrierHitDate" column="BARRIER_HIT_DT"/>
    <property name="amOrPm"         column="AM_OR_PM"/>
    <property name="maxPrice"       column="MAX_PRICE"/>
    <property name="minPrice"       column="MIN_PRICE"/>
</class>

and the two class is as follows

public class ElwInfo extends InstrumentInfo {

private ExchangeInfo exchangeInfo;
private List<ElwUnderlyingAsset> underlyingAssetList;
private ElwTypeInfo elwType;
private OptionTypeInfo optionType;
private ExerciseTypeInfo exerciseType;

private String issueDate;
private String maturityDate;
private Double convertRatio;
private Double strike;
private Double koBarrier;
private Double minRebateRate;
private Double addRebate;

public ElwInfo() {
    exchangeInfo = new ExchangeInfo();
    underlyingAssetList = new ArrayList<ElwUnderlyingAsset>();
    elwType = new ElwTypeInfo();
    optionType = new OptionTypeInfo();
    exerciseType = new ExerciseTypeInfo();
}

public ExchangeInfo getExchangeInfo() {
    return exchangeInfo;
}

public void setExchangeInfo(ExchangeInfo exchangeInfo) {
    this.exchangeInfo = exchangeInfo;
}

public List<ElwUnderlyingAsset> getUnderlyingAssetList() {
    return underlyingAssetList;
}

public void setUnderlyingAssetList(List<ElwUnderlyingAsset> underlyingAssetList) {
    this.underlyingAssetList = underlyingAssetList;
}

public ElwTypeInfo getElwType() {
    return elwType;
}

public void setElwType(ElwTypeInfo elwType) {
    this.elwType = elwType;
}

public OptionTypeInfo getOptionType() {
    return optionType;
}

public void setOptionType(OptionTypeInfo optionType) {
    this.optionType = optionType;
}

public ExerciseTypeInfo getExerciseType() {
    return exerciseType;
}

public void setExerciseType(ExerciseTypeInfo execType) {
    this.exerciseType = execType;
}

public String getIssueDate() {
    return issueDate;
}

public void setIssueDate(String issueDate) {
    this.issueDate = issueDate;
}

public String getMaturityDate() {
    return maturityDate;
}

public void setMaturityDate(String maturityDate) {
    this.maturityDate = maturityDate;
}

public Double getConvertRatio() {
    return convertRatio;
}

public void setConvertRatio(Double convertRatio) {
    this.convertRatio = convertRatio;
}

public Double getStrike() {
    return strike;
}

public void setStrike(Double strike) {
    this.strike = strike;
}

public Double getKoBarrier() {
    return koBarrier;
}

public void setKoBarrier(Double koBarrier) {
    this.koBarrier = koBarrier;
}

public Double getMinRebateRate() {
    return minRebateRate;
}

public void setMinRebateRate(Double minRebateRate) {
    this.minRebateRate = minRebateRate;
}

public Double getAddRebate() {
    return addRebate;
}

public void setAddRebate(Double addRebate) {
    this.addRebate = addRebate;
}

}


public class ElwUnderlyingAsset {

public static class Id implements Serializable {

    private static final long serialVersionUID = -8296040137055492597L;

    private String instrumentCode;
    private String underlyingAssetCode;

    public String getInstrumentCode() {
        return instrumentCode;
    }
    public void setInstrumentCode(String instrumentCode) {
        this.instrumentCode = instrumentCode;
    }
    public String getUnderlyingAssetCode() {
        return underlyingAssetCode;
    }
    public void setUnderlyingAssetCode(String underlyingAssetCode) {
        this.underlyingAssetCode = underlyingAssetCode;
    }

    public boolean equals(Object o) {
        if (o != null && o instanceof Id) {
            Id that = (Id)o;
            return this.instrumentCode.equals(that.instrumentCode) && this.underlyingAssetCode.equals(that.underlyingAssetCode);
        } else {
            return false;
        }
    }

    public int hashCode() {
        if (instrumentCode!= null && underlyingAssetCode != null)
            return instrumentCode.hashCode() + underlyingAssetCode.hashCode();
        else
            return -1;
    }

    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
    }

}

public ElwUnderlyingAsset() {
    this.id = new Id();
}

private Id id;
private Double qty;

public Id getId() {
    return id;
}
public void setId(Id id) {
    this.id = id;
}

public Double getQty() {
    return qty;
}
public void setQty(Double qty) {
    this.qty = qty;
}

public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}

when I call

ElwInfo newInfo = .....// info instance is constructed purely from http request parameters .
getHibernateTemplate().saveOrUpdate(newInfo);

I get the following Generated SQL and Oracle Native Exception. The generated SQL is quite absurd and I cannot figure out what I've done wrong.

Hibernate: 
update
    ELW_UNDERLYING_ASSET 
set
    INSTRUMENT_CD=null 
where
    INSTRUMENT_CD=?

What I expect is

Hibernate: 
update
    ELW_UNDERLYING_ASSET 
set
    QTY = ?
where
    INSTRUMENT_CD=?
    and UNDERLYING_ASSET_CD=?

because table ELW_UNDERLYING_ASSET has composite key (INSTRUMENT_CD, UNDERLYING_ASSET_CD).

If I modify my client code as follows,

ElwInfo newInfo = .....// info instance is constructed purely from http request 
ElwInfo info = (ElwInfo)getHibernateTemplate().get(entityInfo.getClass(),entityInfo.getInstrumentCode());
info.setAddRebate(0.0);
info.getUnderlyingAssetList().clear();
info.getUnderlyingAssetList().addAll(newInfo.getUnderlyingAssetList());
getHibernateTemplate().saveOrUpdate(info);

I get the following generated SQL;

Hibernate: 
update
    ELW_INFO 
set
    INSTRUMENT_NM=?,
    CCY_CD=?,
    ISSUE_DT=?,
    MRTY_DT=?,
    CONVERT_RATIO=?,
    STRIKE=?,
    KO_BARRIER=?,
    MIN_REBATE_RT=?,
    ADD_REBATE=?,
    EXCH_CD=?,
    ELW_TYPE_CD=?,
    OPTION_TYPE_CD=?,
    EXERCISE_TYPE_CD=? 
where
    INSTRUMENT_CD=?

Hibernate: 
update
    ELW_UNDERLYING_ASSET 
set
    INSTRUMENT_CD=null 
where
    INSTRUMENT_CD=? 
    and UNDERLYING_ASSET_CD=?

The first generated sql is ok, but the second one is still wrong though it differs a bit from the previous one.

Thank you for your troubles in advance.

I've added attribute inverse="true" in the bag mapping, and no more errors occurred.

<bag name="underlyingAssetList" inverse="true" cascade="save-update" table="ELW_UNDERLYING_ASSET" lazy="false" >
        <key column="INSTRUMENT_CD"/>
        <one-to-many class="ElwUnderlyingAsset" not-found="ignore"/>
</bag>

I'm quoting the following from the book "Java Persistence with Hibernate, p266".

Without the inverse attribute, Hibernate tries to execute two different SQL statements, both updating the same foreign key column, when you manipulate the link between two instances. By specifying inverse="true", you explicitly tell Hibernate which end of the link it should not synchronize with the database. In this example, you tell Hibernate that it should propagate changes made at the (ElwUnderlyingAsset) end of the association to the database, ignoring changes made only to the (underlyingAssetList) collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM