2016-06-29 5 views
0

Ich muss eine Tabellenkalkulationsdatei in Java abfragen. Ich verwende Apache MetaModel.Apache MetaModel - schlechte Performance-Abfrage Tabellenkalkulation

ich importiert es mit Maven mit

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

Alles funktioniert gut, aber die next() Anweisung, wenn es falsch ein paar Sekunden dauert zurück soll, warum?

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; 
    } 
}} 

UPDATE: Einige weitere Profilierung mit einem Tabellenkalkulationsdokument mit nur 3 Einträge:

-Code ist jetzt:

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"); 

} 

Und die Tabelle in der vorbelastete ist Klassenkonstruktor.

Logs (mit Zeiten) für die letzte einer Reihe von nachfolgenden identischen Abfragen sind:

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: [email protected] 

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 

So ist es zu rekapitulieren dauert etwa eine Sekunde, um das Ergebnis und 4 bis 6 Sekunden für den letzten abrufen Ausführung von next().

+0

Nach dem ersten '' 'next()' '' protokolliert die App '' o.a.m.d.RowPublisherDataSet - Separater Thread für die Veröffentlichung einer Aktion: org.apache.metamodel.excel.XlsxRowPublisherAction @ 3d6e18b4'''. Konnte es auf seine Beendigung warten? –

+0

Wechseln Sie zu github.com/monitorjbl/excel-streaming-reader, lesen Sie das gesamte Dokument beim Start und halten Sie die Spalten, die ich im Speicher in einer Map benötige, damit nachfolgende Abfragen schneller sind. –

Antwort

1

Die Excel DataContext-Implementierung entpackt und analysiert die gezippte .xlsx-Datei im Hintergrund. Dies bedeutet, dass die Methode DataContext.executeQuery(...) schnell zurückkehrt, aber der Aufruf DataSet.next(), der direkt nach dem Warten auf die Daten im Speicher verfügbar ist. Ich sehe keinen Weg, wie Sie das vermeiden können, es ist nur eine Konsequenz von Excel-Dateien, die ziemlich komplex sind.

+0

Ja, aber der Punkt ist, sagen wir eine Zeile als Ergebnis, der erste Aufruf von '' 'Dataset.Next()' '' und der folgende '' 'Dataset.getRow(). getValue (0)' '' Drucken Sie den Wert aus der Tabelle ziemlich schnell (in weniger als einer Sekunde), aber wenn '' '' dataset.next() '' 'das zweite Mal aufgerufen wird (dies wird false zurückgegeben) dauert dieser Aufruf 4 ~ 8 Sekunden. Das ist der Flaschenhals und ich frage mich, warum es passiert. Ich habe auch versucht, das DataSet im Konstruktor zu initialisieren, aber jede nachfolgende Abfrage hat die gleiche Verzögerung. Danke - Schöne Bibliothek übrigens, es ist sehr lesbar :) –

+0

'' 'DataContext''' jetzt im Konstruktor initialisiert, hat Tabelle jetzt nur 3 Einträge => die letzte nächste() ausgeführt dauert immer noch 6 Sekunden ** für jede Abfrage ** –

+1

Das ist eine sehr interessante Beobachtung. Ich würde vorschlagen, es als potenziellen Fehler in der MetaModel-Entwickler-Mailing-Liste anzusprechen. Müsste tiefer hineingehen, um herauszufinden, ob es sich tatsächlich um einen Fehler handelt oder ob es etwas anderes ist. –

Verwandte Themen