Apache MetaModel - таблица запросов с плохой производительностью

Мне нужно запросить файл электронной таблицы на Java. Я использую Apache MetaModel.

Я импортировал его с помощью maven, используя

<dependency>
    <groupId>org.apache.metamodel</groupId>
    <artifactId>MetaModel-excel</artifactId>
    <version>4.5.2</version>
</dependency>

Все работает нормально, но инструкция next(), когда она должна вернуть false, занимает несколько секунд, почему?

import org.apache.metamodel.DataContext;
import org.apache.metamodel.excel.ExcelDataContext;
import org.apache.metamodel.schema.Schema;
import org.apache.metamodel.schema.Column; 
import org.apache.metamodel.schema.Table; 
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.OperatorType; 
import org.apache.metamodel.data.DataSet; 
import org.apache.metamodel.data.Row; 
import org.apache.metamodel.MetaModelException;


public class SpreadsheetReader {

    private File spreadsheet;


    public SpreadsheetReader(String spreadsheetLocation, String spreadsheetName){

        this.spreadsheet = new File( spreadsheetLocation + spreadsheetName );

        if( !"OK".equals( checkSpreadSheet() ) ){
            throw new IllegalStateException("Error in spreadsheet. Cause: "+spreadsheetStatus);
        }

    }


    /** query the excel spreadsheet for the given  ID
    */
    public List<String> query( String givenProgId ){

        List<String> linksArray = new ArrayList<String>();
        int rowCount = 0;

        ExcelConfiguration conf = new ExcelConfiguration( 1, true, true ); // columnNameLineNumber, skipEmptyLines, sEColumns
        DataContext dataContext = new ExcelDataContext( this.spreadsheet, conf );

        System.out.println( "PROFILING >>> "+(new org.joda.time.DateTime())+" START-1" ); // ## 

        Schema schema = dataContext.getDefaultSchema();

        System.out.println( "PROFILING >>> "+(new org.joda.time.DateTime())+" STOP-1" ); // ## 
       // Takes 2 seconds. Will be moved into constructor.

        Table table = schema.getTables()[0];

        Column idsColumn = table.getColumnByName("ProgID");
        Column titlesColumn = table.getColumnByName("Titles");

        Query query = new Query().select(titlesColumn)
                                 .from(table)
                                 .where(idsColumn, OperatorType.EQUALS_TO, givenProgId);

        try( DataSet dataSet = dataContext.executeQuery(query) ){ // try-with-resource, no need to close dataset

            while (dataSet.next()) {

                // the rows are read quite quickly, problem will be when next() is false

                ++rowCount;

                Row currentRow = dataSet.getRow();
                String currentTitle = (String)currentRow.getValue(0);

                linksArray.add( "my-service/titles/"+currentTitle );

                System.out.println( "PROFILING >>> "+(new org.joda.time.DateTime())+" START-2" ); // @@@@@@@
            }
            System.out.println( "PROFILING >>> "+(new org.joda.time.DateTime())+" STOP-2" ); // @@@@@@@ 
            // TAKES ABOUT 6 SECONDS - (Excel file has just 14.779 rows and 114 columns)

        }catch(MetaModelException xx){
            //logger
            throw xx;
        }

        return linksArray;
    }
}}

ОБНОВЛЕНИЕ: Еще немного профилирования с помощью табличного документа всего с 3 записями:

Код сейчас:

try( DataSet dataSet = this.dataContext.executeQuery(query) ){


    // FIRST NEXT() with result => quite fast

    System.out.println( "\n PROFILING >>> "+(new org.joda.time.DateTime())+" START a\n" );
    System.out.println( "\n 88888 NEXT >>> "+(dataSet.next())+" <<<< \n" ); 
    Row currentRow = dataSet.getRow();
    String currentTitle = (String)currentRow.getValue(0);
    System.out.println( "\n READ: "+(new org.joda.time.DateTime())+" >>> "+currentTitle+" \n" );

    System.out.println( "\n PROFILING >>> "+(new org.joda.time.DateTime())+" STOP a\n" );


    // SECOND AND LAST NEXT() => very SLOW

    System.out.println( "\n PROFILING >>> "+(new org.joda.time.DateTime())+" START b\n" );
    System.out.println( "\n 88888 NEXT >>> "+(dataSet.next())+" <<<< \n" );
    System.out.println( "\n PROFILING >>> "+(new org.joda.time.DateTime())+" STOP b\n" );

}

И электронная таблица предварительно загружена в конструктор класса.

Журналы (со временем) для последнего из серии последовательных идентичных запросов:

Jun 30, 2016 10:59:38 AM log my-project.logging.ITVLogger
INFO: CODE00012 - Query on spreadsheet started for ID 123456



PROFILING >>> 2016-06-30T10:59:38.651+01:00 START a

10:59:38.652 [main] INFO  o.a.m.d.RowPublisherDataSet - 
Starting separate thread for publishing action: org.apache.metamodel.excel.XlsxRowPublisherAction@4977e527

88888 NEXT >>> true <<<< 

READ: 2016-06-30T10:59:39.756+01:00 >>> A_TITLE

PROFILING >>> 2016-06-30T10:59:39.756+01:00 STOP a



PROFILING >>> 2016-06-30T10:59:39.756+01:00 START b

88888 NEXT >>> false <<<< 

PROFILING >>> 2016-06-30T10:59:44.735+01:00 STOP b

Итак, подведем итог: получение результата занимает около одной секунды, а последнее выполнение next() — 4–6 секунд.


person Gabe    schedule 29.06.2016    source источник
comment
После первого next() приложение регистрирует o.a.m.d.RowPublisherDataSet - Starting separate thread for publishing action: org.apache.metamodel.excel.XlsxRowPublisherAction@3d6e18b4. Может ли он ждать его окончания?   -  person Gabe    schedule 29.06.2016
comment
Переключение на github.com/monitorjbl/excel-streaming-reader, чтение всего документа при запуске и сохранение нужных мне столбцов в памяти на карте, чтобы последующие запросы выполнялись быстрее   -  person Gabe    schedule 30.06.2016


Ответы (1)


Реализация Excel DataContext выполняет распаковку и анализ заархивированного файла .xlsx в фоновом режиме. Это означает, что метод DataContext.executeQuery(...) возвращается быстро, но вызов DataSet.next(), который приходит сразу после него, должен ждать, пока данные будут доступны в памяти. Я не вижу способа избежать этого, это просто следствие того, что с файлами Excel довольно сложно иметь дело.

person Kasper Sørensen    schedule 29.06.2016
comment
Да, но дело в том, что, скажем, у нас есть только одна строка в результате, первый вызов dataset.next() и последующий dataset.getRow().getValue(0) выводят значение, полученное из электронной таблицы, довольно быстро (менее одной секунды), но когда он вызывает dataset.next() во второй раз (это вернет false) этот вызов занимает 4-8 секунд. Это узкое место, и мне интересно, почему это происходит. Я также пытался инициализировать DataSet в конструкторе, но любой последующий запрос имеет такую ​​же задержку. Спасибо - Хорошая библиотека, кстати, очень читабельна :) - person Gabe; 30.06.2016
comment
DataContext теперь инициализируется в конструкторе, электронная таблица теперь имеет только 3 записи =› последнее выполнение next() по-прежнему занимает 6 секунд для каждого запроса - person Gabe; 30.06.2016
comment
Это очень интересное наблюдение. Я бы предложил поднять это как потенциальную ошибку в списке рассылки разработчиков MetaModel. Нужно будет глубже погрузиться в него, чтобы выяснить, действительно ли это ошибка или что-то еще. - person Kasper Sørensen; 01.07.2016