Change in attribute loading behaviour in v6.3

I’ve just fixed an issue in my application which has arisen since the upgrade to v6.3 and I thought I should pass on some details.

Since the upgrade, when I was attempting to save changes to a particular entity in my application I was receiving an exception regarding an unfetched attribute. This had me scratching my head for quite some time, looking at view definitions etc and debugging the object graph at save time in order to see if I could determine why/how the attribute was not being loaded.

However, I have since discovered that it seems to be due to having a fetch type of ‘lazy’ on this attribute. Changing the fetch type to eager seems to have fixed the problem:


    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "NON_CUSTODY_HOLDING_ID")
    protected NonCustodyHolding nonCustodyHolding;

I wanted to pass this on because I’m not sure if it is a bug or expected behaviour between the version numbers - but it was definitely a breaking issue in my application until I resolved it.

Actually, having LAZY fetch type on reference attributes is absolutely normal and the only recommended option, for all versions of the platform including 6.3.

The fact that you had to switch to EAGER suggests that there is a bug which appears in some use case. Could you explain in details how you load and save these entities? In UI via datasources, through DataManager, EntityManager or maybe in an EntityListener?

Certainly - it was via the UI using data sources. Essentially just from a regular CUBA generated editor, clicking the save button to commit the entity.

Let me know if you need any more info.

Please provide the source code of the two entity classes: class definitions and the parts where they are linked will be enough. And the view which is used in the editor datasource - again in the relation part.

Hopefully this is what you need:


@Listeners("example_HoldingStatusListener")
@NamePattern("%s %s|effectiveStatus,narrative")
@Table(name = "EXAMPLE_HOLDING_STATUS")
@Entity(name = "example$HoldingStatus")
@EnableRestore
public class HoldingStatus extends StandardEntity {
    private static final long serialVersionUID = 9018431515988936586L;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "NON_CUSTODY_HOLDING_ID")
    protected NonCustodyHolding nonCustodyHolding;

}

@NamePattern(" %s|investment")
@Table(name = "EXAMPLE_NON_CUSTODY_HOLDING")
@Entity(name = "example$NonCustodyHolding")
@EnableRestore
public class NonCustodyHolding extends StandardEntity {
    private static final long serialVersionUID = 248213542069481046L;

    @Composition
    @OnDelete(DeletePolicy.CASCADE)
    @OneToMany(mappedBy = "nonCustodyHolding", cascade = CascadeType.PERSIST)
    protected Set<HoldingStatus> statusHistory;

}

  <view class="com.example.example.investment.HoldingStatus"
          extends="_local"
          name="holdingStatus-view">
        <property name="nonCustodyHolding">
            <property name="investment">
                <property name="fullName"/>
                <property name="currencyMask">
                    <property name="isoCode"/>
                    <property name="fullName"/>
                    <property name="symbolPrefix"/>
                </property>
                <property name="underlyingInstrument">
                    <property name="primaryIdentifier"/>
                    <property name="currency">
                        <property name="isoCode"/>
                        <property name="fullName"/>
                        <property name="symbolPrefix"/>
                    </property>
                    <property name="name"/>
                    <property name="description"/>
                </property>
                <property name="nameMask"/>
                <property name="identifierMask"/>
            </property>
        </property>

Could you try to remove cascade = CascadeType.PERSIST from the statusHistory attribute?

You probably added it manually for some reason, because it is usually not needed and Studio doesn’t do it. The rest in entities is standard.

If removing the cascade persist doesn’t help, the cause is somewhere in datasources and views. Do you have a NonCustodyHolding editor with a table containing the list of HoldingStatus, and edit HoldingStatus in a separate editor? Could you provide the datasources section from both XML descriptors? And the view from NonCustodyHolding editor?

I’ve done the above and I’m afraid it brought the error back. It is very likely I put the Cascade Persist on myself, as I was seeing quite a few key exceptions saving data until I did this.

You are right, there is an editor for the holding entity which shows a list of the status items, and a separate editor for each. The XML for the data sources section of each is included below:


    <dsContext>
        <datasource id="nonCustodyHoldingDs"
                    class="com.example.investment.NonCustodyHolding"
                    view="nonCustodyHolding-view">
            <collectionDatasource id="statusHistoryDs"
                                  property="statusHistory"/>
            <collectionDatasource id="hasDocumentsDs"
                                  property="hasDocuments"/>
            <datasource id="moneyDs"
                        property="cost"/>
        </datasource>
        <collectionDatasource id="currenciesDs"
                              class="com.example.investment.Currency">
            <query>
                <![CDATA[select e from example$Currency e]]>
            </query>
        </collectionDatasource>
    </dsContext>

    <dsContext>
        <datasource id="holdingStatusDs"
                    class="com.example.investment.HoldingStatus"
                    view="holdingStatus-view"/>
        <collectionDatasource id="currenciesDs"
                              class="com.example.investment.Currency">
            <query>
                <![CDATA[select e from example$Currency e]]>
            </query>
        </collectionDatasource>
        <collectionDatasource id="holdingStatusTypesDs"
                              class="com.example.investment.HoldingStatusType">
            <query>
                <![CDATA[select e from example$HoldingStatusType e]]>
            </query>
        </collectionDatasource>
    </dsContext>

Unfortunately, I don’t see anything unusual in the code you provided. Maybe the cause is in a particular structure of object relations or views (not only Holding and Status, but other entities included to the views too).

We would really appreciate if you could extract a part of your project to a separate sample application where the problem is reproduced.