Massive performance problem with transient properties

Hi all,

I am having quite a headache in figuring out a performance problem but I think the cause is rooted in transient properties.
I have an entity Company with some fields and a 1:n relation to Employees. I then have a transient property “numberOfTechEmployees” which is just a method that does some filtering and counting on the employees using streams. The reason this is a transient property is that I want to include it in tables easily and also be able to sort the column (which is not possible with generated columns).

Then there is an entity Projects and it has a reference to a Company.
If I have a table on my screen and want to list projects including a column for the company and I have 500 projects then on loading the data CUBA tries to merge the data into the dataContext. I think merging will only look at the loaded properties meaning the ones I specified in the view of the dataLoader. It does so by using
entityStates.isLoaded(srcEntity, propertyName)

The problem is that transient properties seem to be loaded sometimes, at least I have one that shows as loaded and another that is not and both are not included in the view, so the merging will call the getter for that property. If the Company now has 8.000 employees it may take one second to execute the filtering/streaming which is done for all 500 companies that occur in the projects. And that is even if I don’t need the information in the table at all.

Maybe I am completely wrong here but is there a way to exclude transient properties from getting evaluated during the merge? I take my findings from the DataContextImpl.mergeState() method from line 249:

for (MetaProperty property : metaClass.getProperties()) {
            String propertyName = property.getName();
            if (!property.getRange().isClass()                                             // local
                    && property != primaryKeyProperty                                      // not PK
                    && (srcNew || entityStates.isLoaded(srcEntity, propertyName))          // loaded src
                    && (dstNew || entityStates.isLoaded(dstEntity, propertyName))) {       // loaded dst

                Object value = srcEntity.getValue(propertyName);

Thanks for any pointers! :slight_smile:

edit: I have tried to annotate the properties/fields and also the getter with different variations of @Transient and @MetaProperty but no luck so far

edit2: Out of curiosity I would also like to know why the Company needs to be in the dataContext at all? I am just showing a list of projects and want to edit those. Is that because everything that can be seen may be edited by the user? But even if I change the company in a project that is not a change in the company itself. I am sure there is a good reason so I would be glad to know :wink:

Hi,

  1. You can specify MetaProperty#related attribute to let the framework know, which persistent attributes does numberOfTechEmployees depend on.
@MetaProperty(related = "employees")
public Integer getNumberOfTechEmployees() { 
... 
}

https://doc.cuba-platform.com/manual-7.2/entity_attr_annotations.html#metaProperty_annotation

  1. You can avoid lazy loading of the employees collection inside of the method by checking, whether this collection is loaded, using the static com.haulmont.cuba.core.global.PersistenceHelper#isLoaded method.
@MetaProperty
public Integer getNumberOfTechEmployees() { 
    if (!PersistenceHelper.isLoaded(this, "employees")) {
        return null;
    }
    // calculate
... 
}

https://doc.cuba-platform.com/manual-7.2/persistenceHelper.html

Thank you very much, adding the related attribute and also removing it from the view now causes the “isLoaded()” method to evaluate to false for the transient properties.
I am still not sure why the transient/read-only properties need to be copied in the dataContext, but your solution works fine now. Thanks again!