Hi
Better to not override logics by following reasons:
- Table have own sort that not comparing to SQL-like sort (its likely random 50 rows per page, each sorted in memory, but no guarantee that jpql loaded that data struct strictly depending on sql logics)
- There is hard to maintain or own logics of selection that “first” page. E.g. First page sorted by name in table != First page sorted by name in SQL.
- We cant load first page of table without moving client to the first page of table.
But, if you still want to load only “first page” instead of current, there is most likely logics for Exporter:
package com.company.jmixcustomexport.export;
import com.company.jmixcustomexport.entity.User;
import com.company.jmixcustomexport.screen.user.UserBrowse;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.jmix.core.DataManager;
import io.jmix.core.Metadata;
import io.jmix.core.querycondition.Condition;
import io.jmix.gridexportui.exporter.ExportMode;
import io.jmix.gridexportui.exporter.ExporterSortHelper;
import io.jmix.gridexportui.exporter.entitiesloader.AllEntitiesLoader;
import io.jmix.gridexportui.exporter.entitiesloader.AllEntitiesLoaderFactory;
import io.jmix.gridexportui.exporter.json.JsonExporter;
import io.jmix.ui.component.DataGrid;
import io.jmix.ui.component.Table;
import io.jmix.ui.component.impl.DataGridSettingsUtils;
import io.jmix.ui.download.ByteArrayDataProvider;
import io.jmix.ui.download.DownloadFormat;
import io.jmix.ui.download.Downloader;
import io.jmix.ui.model.DataComponents;
import io.jmix.ui.model.DataLoader;
import io.jmix.ui.model.ScreenData;
import io.jmix.ui.screen.StandardLookup;
import io.jmix.ui.screen.UiControllerUtils;
import io.jmix.ui.sys.UiControllerMeta;
import io.jmix.ui.sys.UiControllerReflectionInspector;
import io.jmix.ui.sys.UiDescriptorUtils;
import io.jmix.ui.widget.AppUIUtils;
import io.jmix.uidata.filter.UiDataFilterMetadataTools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import static liquibase.repackaged.net.sf.jsqlparser.util.validation.metadata.NamedObject.table;
/**
* Custom JSON exporter implemented as a Spring Bean component.
* This class handles the export of entities to JSON format with custom entity loading logic.
*/
@Component("custom_JsonExporter")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class CustomJsonExporter extends JsonExporter {
private final DataManager dataManager;
@Qualifier("customExecutor")
@Autowired
private ExecutorService executorService;
private final AllEntitiesLoaderFactory allEntitiesLoaderFactory;
public CustomJsonExporter(Metadata metadata, AllEntitiesLoaderFactory allEntitiesLoaderFactory, DataManager dataManager) {
super(metadata, allEntitiesLoaderFactory);
this.allEntitiesLoaderFactory = allEntitiesLoaderFactory;
this.dataManager = dataManager;
}
@Override
protected Collection<Object> getItems(DataGrid<Object> dataGrid, ExportMode exportMode) {
// OR your custom logics to load entities from grid
return ExportMode.CURRENT_PAGE == exportMode
? dataGrid.getItems().getItems().collect(Collectors.toList())
: dataGrid.getSelected();
}
@Override
protected Collection<Object> getItems(Table<Object> table, ExportMode exportMode) {
// OR your custom logics to load entities from table
return ExportMode.CURRENT_PAGE == exportMode
? table.getItems().getItems()
: table.getSelected();
}
@Override
public void exportDataGrid(Downloader downloader, DataGrid<Object> dataGrid, ExportMode exportMode) {
if (exportMode == ExportMode.ALL_ROWS) {
exportDataGridAll(downloader, dataGrid, exportMode);
} else {
exportDataGridSelected(downloader, dataGrid, exportMode);
}
}
private void exportDataGridAll(Downloader downloader, DataGrid<Object> dataGrid, ExportMode exportMode) {
Gson gson = createGsonForSerialization();
JsonArray jsonElements = new JsonArray();
CompletableFuture.runAsync(() -> {
AllEntitiesLoader entitiesLoader = allEntitiesLoaderFactory.getEntitiesLoader();
entitiesLoader.loadAll(dataGrid.getItems(),
context -> {
JsonObject jsonObject = createJsonObjectFromEntity(dataGrid, context.getEntity());
jsonElements.add(jsonObject);
return true;
},
ExporterSortHelper.getSortOrder(dataGrid.getSortOrder()));
}, executorService
).thenAccept(e -> {
downloader.download(new ByteArrayDataProvider(gson.toJson(jsonElements).getBytes(StandardCharsets.UTF_8),
uiProperties.getSaveExportedByteArrayDataThresholdBytes(), coreProperties.getTempDir()),
getFileName(dataGrid) + ".json", DownloadFormat.JSON);
super.exportDataGrid(downloader, dataGrid, exportMode);
});
}
private void exportDataGridSelected(Downloader downloader, DataGrid<Object> dataGrid, ExportMode exportMode) {
Gson gson = createGsonForSerialization();
JsonArray jsonElements = new JsonArray();
Collection<Object> items = getItems(dataGrid, exportMode);
for (Object entity : items) {
JsonObject jsonObject = createJsonObjectFromEntity(dataGrid, entity);
jsonElements.add(jsonObject);
}
downloader.download(new ByteArrayDataProvider(gson.toJson(jsonElements).getBytes(StandardCharsets.UTF_8),
uiProperties.getSaveExportedByteArrayDataThresholdBytes(), coreProperties.getTempDir()),
getFileName(dataGrid) + ".json", DownloadFormat.JSON);
super.exportDataGrid(downloader, dataGrid, exportMode);
}
@Override
public void exportTable(Downloader downloader, Table<Object> table, ExportMode exportMode) {
var res2 = UiControllerUtils.getScreen(table.getFrame().getFrameOwner());
if(res2 instanceof UserBrowse) {
ScreenData screenData = UiControllerUtils.getScreenData(table.getFrame().getFrameOwner());
DataLoader usersDl = screenData.getLoader("usersDl");
List<User> userList = dataManager.load(User.class)
.condition(Objects.requireNonNull(usersDl.getCondition())) // same condition as in the screen
.sort(ExporterSortHelper.getSortOrder(table.getSortInfo()))
.maxResults(50) // always one page
.firstResult(0) // always first page
.list();
Gson gson = createGsonForSerialization();
JsonArray jsonElements = new JsonArray();
for (Object entity : userList) {
JsonObject jsonObject = createJsonObjectFromEntity(table, entity);
jsonElements.add(jsonObject);
}
downloader.download(new ByteArrayDataProvider(gson.toJson(jsonElements).getBytes(StandardCharsets.UTF_8),
uiProperties.getSaveExportedByteArrayDataThresholdBytes(), coreProperties.getTempDir()),
getFileName(table) + ".json", DownloadFormat.JSON);
return;
}
if (exportMode == ExportMode.ALL_ROWS) {
exportTableAll(downloader, table, exportMode);
} else {
exportTableSelected(downloader, table, exportMode);
}
}
private void exportTableAll(Downloader downloader, Table<Object> table, ExportMode exportMode) {
Gson gson = createGsonForSerialization();
JsonArray jsonElements = new JsonArray();
CompletableFuture.runAsync(() -> {
AllEntitiesLoader entitiesLoader = allEntitiesLoaderFactory.getEntitiesLoader();
entitiesLoader.loadAll(table.getItems(),
context -> {
JsonObject jsonObject = createJsonObjectFromEntity(table, context.getEntity());
jsonElements.add(jsonObject);
return true;
},
ExporterSortHelper.getSortOrder(table.getSortInfo()));
}, executorService
).thenAccept(e -> {
downloader.download(new ByteArrayDataProvider(gson.toJson(jsonElements).getBytes(StandardCharsets.UTF_8),
uiProperties.getSaveExportedByteArrayDataThresholdBytes(), coreProperties.getTempDir()),
getFileName(table) + ".json", DownloadFormat.JSON);
});
}
private void exportTableSelected(Downloader downloader, Table<Object> table, ExportMode exportMode) {
Gson gson = createGsonForSerialization();
JsonArray jsonElements = new JsonArray();
Collection<Object> items = getItems(table, exportMode);
for (Object entity : items) {
JsonObject jsonObject = createJsonObjectFromEntity(table, entity);
jsonElements.add(jsonObject);
}
downloader.download(new ByteArrayDataProvider(gson.toJson(jsonElements).getBytes(StandardCharsets.UTF_8),
uiProperties.getSaveExportedByteArrayDataThresholdBytes(), coreProperties.getTempDir()),
getFileName(table) + ".json", DownloadFormat.JSON);
}
@Override
protected JsonObject createJsonObjectFromEntity(DataGrid<Object> dataGrid, Object entity) {
// override if you want custom json creation for dataGrid
return super.createJsonObjectFromEntity(dataGrid, entity);
}
@Override
protected JsonObject createJsonObjectFromEntity(Table<Object> table, Object entity) {
// override if you want custom json creation for Table
return super.createJsonObjectFromEntity(table, entity);
}
}
Test project (jmix 1.6):
jmixcustomexport.zip (285.1 KB)
This is example, how you can “imitate” first page load logics. Its ok for filter, but again sort - is problem. Or, otherwise, instead of in-memory logics you can duplicates sort by in JPQL or loader in screen and then pages would be same. (force avoid jmix table in-memory sorter)
By the way, i won’t recommend maintain own logics and “generic” solution by you own. If you still want this solution, check our Jmix Assistant, probably it would helps you faster.
Best regards,
Dmitry