Collection Datasource Query based

Hey Guys,

In my OrderLineEdit, I loop through all the elements of the datasource I have till I find the row that I need then I break out of the loop. As functionality, it’s work perfectly for me. However, I think later on it will be slow, since it will have a complexity of O(n). I think getting the result from a query would be much faster, but I don’t know how to pass a value to a query.

here’s my function that I am telling you about:


package com.company.sales.web.orderline;

import com.company.sales.entity.*;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.impl.BigDecimalDatatype;
import com.haulmont.cuba.gui.components.AbstractEditor;
import com.haulmont.cuba.gui.components.PickerField;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;

import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.*;
import java.math.BigDecimal;
import java.util.Map;
import java.util.UUID;

public class OrderLineEdit extends AbstractEditor<OrderLine> {
    @Named("fieldGroup.product")
    private PickerField productField;

    @Inject
    private Datasource<OrderLine> orderLineDs;
    @Inject
    private CollectionDatasource<Price, UUID> pricesDs;

    PriceType priceType = new PriceType();


    private BigDecimal getPrice(Product product, PriceType priceType){
        BigDecimal tmp = BigDecimal.ZERO;
        for(Price price : pricesDs.getItems()){
            if(price.getPricetype().getType_no().equals(priceType.getType_no()) && price.getProduct().getCode().equals(product.getCode())){
                tmp = price.getPrice();
                break;
            }
        }
        return tmp;
    }

    @Override
    public void init(Map<String, Object> params) {
        productField.addLookupAction();
        productField.addOpenAction();

        pricesDs.refresh();
    }

    @Override
    protected void postInit() {

        try{
            priceType = getItem().getOrder().getCustomer().getPricetype();
        }
        catch (NullPointerException e){
            throw new NullPointerException("Customer Field is Empty");
        }

        orderLineDs.addItemPropertyChangeListener(e -> {
            if(e.getProperty().equals("product")){
                BigDecimal price = getPrice(getItem().getProduct(), priceType);
                getItem().setPrice(price);
            }
        });
    }
}

I am talking about the function getPrice(). I know that I should use setQuery function for priceDs, but how can I pass more than one value to it and what is the correct way to write it. Also, would I need to refresh the datasource before setting its query or just set it directly? Finally, what is the correct & efficient way to use for the setQuery syntax.

Hi Karim,

The way you search for price is really very ineffective. Please keep in mind that you need datasources only when you want to dispaly or edit data in visual components. Otherwise, use queries via DataManager or via your own services.

Please look at the Data Manipulation sample application (it is also available in Studio on the Samples tab). It shows how to execute queries on the middleware or right from screen controllers.

Thank you Konstantin for your reply and I am really sorry for my late one,

I have tried doing something similar to the example of the Data Manager, but I getting error in the Intellij IDEA so I don’t think it will compile at the cuba.

can you please tell me if my datamanger statement and my for loop give the same functionality.

Code :


    private BigDecimal getPrice(Product product, PriceType priceType){
        BigDecimal tmp = BigDecimal.ZERO;
        String caption = "????? ??? ????? ???? ?????";
        String message = "Please add the Product in the Price Type";

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("priceType", priceType);
        parameters.put("product",product);


        LoadContext loadContext = LoadContext.create(Price.class).setQuery(LoadContext.createQuery("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product").setParameters(parameters)).setView("OrderLineEdit");

        for(Price price : pricesDs.getItems()){
            if(price.getPricetype().getType_no().equals(priceType.getType_no()) && price.getProduct().getCode().equals(product.getCode())){
                tmp = price.getPrice();
                break;
            }
        }

        if(tmp.equals(BigDecimal.ZERO)){
            showNotification(getMessage(caption), getMessage(message), NotificationType.ERROR);
        }
        return tmp;
    }

Thanks

Hi Karim,

The query looks correct, but you didn’t execute it. You have to add an actual invocation of data manager like:


Price price = dataManager.load(loadContext);

If you get an exception, attach the stacktrace here.

When I tried to compile I got 2 errors:


:app-web:compileJava/home/karim/studio-projects/sales_Inpaco/modules/web/src/com/company/sales/web/orderline/OrderLineEdit.java:46: error: 'void' type not allowed here
        LoadContext loadContext = LoadContext.create(Price.class).setQuery(LoadContext.createQuery("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product").setParameters(parameters)).setView("OrderLineEdit");
                                                                                                                                                                                                             ^
/home/karim/studio-projects/sales_Inpaco/modules/web/src/com/company/sales/web/orderline/OrderLineEdit.java:48: error: incompatible types: Entity cannot be converted to Price
        Price price1 = dataManager.load(loadContext);

then I changed this line, which fixed one error:


LoadContext<Price> loadContext = LoadContext.create(Price.class).setQuery(LoadContext.createQuery("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product").setParameters(parameters)).setView("OrderLineEdit");

I just added the type of the loadContext. Yet I am still getting another error.


:app-web:compileJava/home/karim/studio-projects/sales_Inpaco/modules/web/src/com/company/sales/web/orderline/OrderLineEdit.java:45: error: 'void' type not allowed here
        LoadContext<Price> loadContext = LoadContext.create(Price.class).setQuery(LoadContext.createQuery("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product").setParameters(parameters)).setView("OrderLineEdit");

which is the first error from before. I don’t know what’s cause of this error.

I have also attached the file with the entire code

OrderLineEdit.java (3.1K)

The problem is the LoadContext.Query#setParameters method does not return the Query instance for chaining. We have fixed it in platform 6.4.

You can use the setParameter() methods instead of passing the whole map of parameters:


LoadContext<Price> loadContext = LoadContext.create(Price.class)
    .setQuery(
        LoadContext.createQuery("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product")
            .setParameter("priceType", priceType)
            .setParameter("product", product))
    .setView("OrderLineEdit");

Or just use separate assignments, it will be more clear:


LoadContext<Price> loadContext = LoadContext.create(Price.class);
LoadContext.Query query = loadContext.setQueryString("SELECT p FROM sales$Price p WHERE p.pricetype.id = :priceType AND p.product.id = :product");
query.setParameter("priceType", priceType);
query.setParameter("product", product);
loadContext.setView("OrderLineEdit");