Skip to content

Commit

Permalink
perf: increase performance when exporting large datasets
Browse files Browse the repository at this point in the history
increase performance by avoid calling sheet.shiftRows() by duplicating
the sheet into a temporal one, exporting the data and then appending
the last part of the sheet before continuing with the footers
  • Loading branch information
mlopezFC authored and paodb committed Nov 28, 2023
1 parent 8c06691 commit 2edfcea
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@
/** */
package com.flowingcode.vaadin.addons.gridexporter;

import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid.Column;
import com.vaadin.flow.data.binder.BeanPropertySet;
import com.vaadin.flow.data.binder.PropertySet;
import com.vaadin.flow.data.provider.DataProvider;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
Expand All @@ -39,7 +33,6 @@
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
Expand All @@ -58,6 +51,12 @@
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid.Column;
import com.vaadin.flow.data.binder.BeanPropertySet;
import com.vaadin.flow.data.binder.PropertySet;
import com.vaadin.flow.data.provider.DataProvider;

/**
* @author mlope
Expand Down Expand Up @@ -107,10 +106,17 @@ public InputStream createInputStream() {

// initialize the data range with tne coordinates of tha data placeholder cell
CellRangeAddress dataRange = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), cell.getColumnIndex(), cell.getColumnIndex());
fillData(sheet, cell, exporter.grid.getDataProvider(), dataRange, titleCell != null);

Sheet tempSheet = wb.cloneSheet(exporter.sheetNumber);

int lastRow = fillData(sheet, cell, exporter.grid.getDataProvider(), dataRange, titleCell != null);

applyConditionalFormattings(sheet, dataRange);

copyBottomOfSheetStartingOnRow(wb, tempSheet, sheet, cell.getRowIndex()+1, lastRow);

wb.removeSheetAt(exporter.sheetNumber + 1);

cell = findCellWithPlaceHolder(sheet, exporter.footersPlaceHolder);
List<Pair<String, Column<T>>> footers = getGridFooters(exporter.grid);
if (cell != null) {
Expand Down Expand Up @@ -166,6 +172,54 @@ public void run() {
return in;
}

private void copyBottomOfSheetStartingOnRow(Workbook workbook, Sheet sourceSheet,
Sheet targetSheet, int rowIndex, int targetRow) {
int fRow = rowIndex;
int lRow = sourceSheet.getLastRowNum();
for (int iRow = fRow; iRow <= lRow; iRow++) {
Row row = sourceSheet.getRow(iRow);
Row myRow = targetSheet.createRow(targetRow++);
if (row != null) {
short fCell = row.getFirstCellNum();
short lCell = row.getLastCellNum();
for (int iCell = fCell; iCell < lCell; iCell++) {
Cell cell = row.getCell(iCell);
Cell newCell = myRow.createCell(iCell);
newCell.setCellStyle(cell.getCellStyle());
if (cell != null) {
switch (cell.getCellType()) {
case BLANK:
newCell.setCellValue("");
break;

case BOOLEAN:
newCell.setCellValue(cell.getBooleanCellValue());
break;

case ERROR:
newCell.setCellErrorValue(cell.getErrorCellValue());
break;

case FORMULA:
newCell.setCellFormula(cell.getCellFormula());
break;

case NUMERIC:
newCell.setCellValue(cell.getNumericCellValue());
break;

case STRING:
newCell.setCellValue(cell.getStringCellValue());
break;
default:
newCell.setCellFormula(cell.getCellFormula());
}
}
}
}
}
}

private void applyConditionalFormattings(Sheet sheet, CellRangeAddress targetCellRange) {
SheetConditionalFormatting sheetCondFormatting = sheet.getSheetConditionalFormatting();

Expand All @@ -177,7 +231,7 @@ private void applyConditionalFormattings(Sheet sheet, CellRangeAddress targetCel

}

private void fillData(
private int fillData(
Sheet sheet, Cell dataCell, DataProvider<T, ?> dataProvider, CellRangeAddress dataRange, boolean titleExists) {
Stream<T> dataStream = obtainDataStream(dataProvider);

Expand All @@ -188,11 +242,6 @@ private void fillData(
t -> {
if (notFirstRow[0]) {
CellStyle cellStyle = startingCell[0].getCellStyle();
int lastRow = sheet.getLastRowNum();
sheet.shiftRows(
startingCell[0].getRowIndex() + (titleExists ? 1 : 0),
lastRow,
(titleExists ? 1 : 0));
Row newRow = sheet.createRow(startingCell[0].getRowIndex() + 1);
startingCell[0] = newRow.createCell(startingCell[0].getColumnIndex());
startingCell[0].setCellStyle(cellStyle);
Expand All @@ -205,6 +254,7 @@ private void fillData(
// since we initialized the cell range with the data placeholder cell, we use
// the existing 'getLastColumn' to keep the offset of the data range
dataRange.setLastColumn(dataRange.getLastColumn() + exporter.getColumns().size() - 1);
return startingCell[0].getRowIndex();
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*-
* #%L
* Grid Exporter Add-on
* %%
* Copyright (C) 2022 - 2023 Flowing Code
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.flowingcode.vaadin.addons.gridexporter;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.poi.EncryptedDocumentException;
import com.flowingcode.vaadin.addons.demo.DemoSource;
import com.github.javafaker.Faker;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.Grid.Column;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;

@DemoSource
@PageTitle("Grid Exporter Addon Big Dataset Demo")
@Route(value = "gridexporter/bigdataset", layout = GridExporterDemoView.class)
@SuppressWarnings("serial")
public class GridExporterBigDatasetDemo extends Div {

public GridExporterBigDatasetDemo() throws EncryptedDocumentException, IOException {
Grid<Person> grid = new Grid<>(Person.class);
grid.removeAllColumns();
grid.setColumnReorderingAllowed(true);
Column<Person> nameCol = grid.addColumn("name").setHeader("Name");
Column<Person> lastNameCol = grid.addColumn("lastName").setHeader("Last Name");
Column<Person> budgetCol = grid.addColumn(item -> "$" + item.getBudget()).setHeader("Budget");
BigDecimal[] total = new BigDecimal[1];
total[0] = BigDecimal.ZERO;
List<Person> persons =
IntStream.range(0, 7000)
.asLongStream()
.mapToObj(
number -> {
Faker faker = new Faker();
Double budget = faker.number().randomDouble(2, 10000, 100000);
total[0] = total[0].add(BigDecimal.valueOf(budget));
budgetCol.setFooter("$" + total[0]);
return new Person(
faker.name().firstName(),
faker.name().lastName(),
faker.number().numberBetween(15, 50),
budget);
}).collect(Collectors.toList());
grid.setItems(query->persons.stream().skip(query.getOffset()).limit(query.getLimit()));
grid.setWidthFull();
this.setSizeFull();
GridExporter<Person> exporter = GridExporter.createFor(grid);
exporter.setExportValue(budgetCol, item -> "" + item.getBudget());
exporter.setColumnPosition(lastNameCol, 1);
exporter.setTitle("People information");
exporter.setFileName(
"GridExport" + new SimpleDateFormat("yyyyddMM").format(Calendar.getInstance().getTime()));
add(grid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public GridExporterDemoView() {
addDemo(GridExporterCustomLinkDemo.class);
addDemo(GridExporterCustomColumnsDemo.class);
addDemo(GridExporterHierarchicalDataDemo.class);
addDemo(GridExporterBigDatasetDemo.class);
setSizeFull();
}
}

0 comments on commit 2edfcea

Please sign in to comment.