Extract CsvReader - move DefaultCsvCallback and add module-info

This commit is contained in:
Rob Bygrave
2022-08-25 17:49:11 +12:00
parent 216c43eea6
commit 1aac01ead0
17 changed files with 19 additions and 200 deletions
@@ -0,0 +1,78 @@
package io.ebean.csv.reader;
import io.ebean.Database;
/**
* Provides callback methods for customisation of CSV processing.
* <p>
* You can provide your own CsvCallback implementation to customise the CSV
* processing. It is expected that the DefaultCsvCallback provides a good base
* class that you can extend.
*/
public interface CsvCallback<T> {
/**
* The processing is about to begin.
* <p>
* Typically the callback will create a transaction, set batch mode, batch
* size etc.
* </p>
*/
void begin(Database database);
/**
* Read the header row.
* <p>
* This is only called if {@link CsvReader#setHasHeader(boolean, boolean)} has
* been set to true.
* </p>
*
* @param line the header line content.
*/
void readHeader(String[] line);
/**
* Check that the row should be processed - return true to process the row or
* false to ignore the row. Gives ability to handle bad data... empty rows etc
* and ignore it rather than fail.
*/
boolean processLine(int row, String[] line);
/**
* Called for each bean after it has been loaded from the CSV content.
* <p>
* This allows you to process the bean however you like.
* </p>
* <p>
* When you use a CsvCallback the CsvReader *WILL NOT* create a transaction
* and will not save the bean for you. You have complete control and must do
* these things yourself (if that is want you want).
* </p>
*
* @param row the index of the content being processed
* @param line the content that has been used to load the bean
* @param bean the entity bean after it has been loaded from the csv content
*/
void processBean(int row, String[] line, T bean);
/**
* The processing has ended successfully.
* <p>
* Typically the callback will commit the transaction.
* </p>
*/
void end(int row);
/**
* The processing has ended due to an error.
* <p>
* This gives the callback the opportunity to rollback the transaction if one
* was created.
* </p>
*
* @param row the row that the error has occurred on
* @param e the error that occurred
*/
void endWithError(int row, Exception e);
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.text.csv;
package io.ebean.csv.reader;
import io.ebean.Database;
import io.ebean.bean.EntityBean;
@@ -6,9 +6,6 @@ import io.ebean.plugin.BeanType;
import io.ebean.plugin.ExpressionPath;
import io.ebean.text.StringParser;
import io.ebean.text.TextException;
import io.ebean.text.csv.CsvCallback;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.csv.DefaultCsvCallback;
import java.io.Reader;
import java.sql.Types;
@@ -24,7 +21,7 @@ import java.util.Locale;
/**
* Implementation of the CsvReader
*/
public class TCsvReader<T> {//implements CsvReader<T> {
public class CsvReader<T> {
private static final TimeStringParser TIME_PARSER = new TimeStringParser();
@@ -52,9 +49,9 @@ public class TCsvReader<T> {//implements CsvReader<T> {
private boolean addPropertiesFromHeader;
public TCsvReader(Database server, BeanType<T> descriptor) {
public CsvReader(Database server, Class<T> type) {
this.server = server;
this.descriptor = descriptor;
this.descriptor = server.pluginApi().beanType(type);
}
////@Override
@@ -229,7 +226,6 @@ public class TCsvReader<T> {//implements CsvReader<T> {
private void addPropertiesFromHeader(String[] line) {
for (String aLine : line) {
ExpressionPath elProp = descriptor.expressionPath(aLine);
//ElPropertyValue elProp = descriptor.elGetValue(aLine);
if (elProp == null) {
throw new TextException("Property [" + aLine + "] not found");
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.text.csv;
package io.ebean.csv.reader;
// Original name: au.com.bytecode.opencsv.CSVReader
// rbygrave: Made some Java Generics tweaks to remove warnings
@@ -0,0 +1,197 @@
package io.ebean.csv.reader;
import io.ebean.Database;
import io.ebean.EbeanVersion;
import io.ebean.Transaction;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.INFO;
/**
* Provides the default implementation of CsvCallback.
* <p>
* This handles transaction creation (if no current transaction existed) and
* transaction commit or rollback on error.
* </p>
* <p>
* For customising the processing you can extend this object and override the
* appropriate methods.
* </p>
*
* @param <T>
*/
public class DefaultCsvCallback<T> implements CsvCallback<T> {
private static final System.Logger log = EbeanVersion.log;
/**
* The transaction to use (if not using CsvCallback).
*/
protected Transaction transaction;
/**
* Flag set when we created the transaction.
*/
protected boolean createdTransaction;
/**
* The EbeanServer used to save the beans.
*/
protected Database server;
/**
* Used to log a message to indicate progress through large files.
*/
protected final int logInfoFrequency;
/**
* The batch size used when saving the beans.
*/
protected final int persistBatchSize;
protected boolean getGeneratedKeys = true;
/**
* The time the process started.
*/
protected long startTime;
/**
* The execution time of the process.
*/
protected long exeTime;
/**
* Construct with a default batch size of 30 and logging info messages every
* 1000 rows.
*/
public DefaultCsvCallback() {
this(30, 1000);
}
/**
* Construct with explicit batch size and logging info frequency.
*/
public DefaultCsvCallback(int persistBatchSize, int logInfoFrequency) {
this.persistBatchSize = persistBatchSize;
this.logInfoFrequency = logInfoFrequency;
}
/**
* Create a transaction if required.
*/
@Override
public void begin(Database server) {
this.server = server;
this.startTime = System.currentTimeMillis();
initTransactionIfRequired();
}
/**
* Override to read the heading line.
* <p>
* This is only called if {@link CsvReader#setHasHeader(boolean, boolean)} is
* set to true.
* <p>
* By default this does nothing (effectively ignoring the heading).
*/
@Override
public void readHeader(String[] line) {
}
/**
* Validate that the content is valid and return false if the row should be
* ignored.
* <p>
* By default this just returns true.
* </p>
* <p>
* Override this to add custom validation logic returning false if you want
* the row to be ignored. For example, if all the content is empty return
* false to ignore the row (rather than having the processing fail with some
* error).
* </p>
*/
@Override
public boolean processLine(int row, String[] line) {
return true;
}
/**
* Will save the bean.
* <p>
* Override this method to customise the bean (set additional properties etc)
* or to control the saving of other related beans (when you can't/don't want
* to use Cascade.PERSIST etc).
* </p>
*/
@Override
public void processBean(int row, String[] line, T bean) {
// assumes single bean or Cascade.PERSIST will save any
// related beans (e.g. customer -> customer.billingAddress
server.save(bean, transaction);
if (logInfoFrequency > 0 && (row % logInfoFrequency == 0)) {
log.log(DEBUG, "processed {0} rows", row);
}
}
/**
* Commit the transaction if one was created.
*/
@Override
public void end(int row) {
commitTransactionIfCreated();
exeTime = System.currentTimeMillis() - startTime;
log.log(INFO, "Csv finished, rows[{0}] exeMillis[{1}]", row, exeTime);
}
/**
* Rollback the transaction if one was created.
*/
@Override
public void endWithError(int row, Exception e) {
rollbackTransactionIfCreated(e);
}
/**
* Create a transaction if one is not already active and set its batch mode
* and batch size.
*/
protected void initTransactionIfRequired() {
transaction = server.currentTransaction();
if (transaction == null || !transaction.isActive()) {
transaction = server.beginTransaction();
createdTransaction = true;
if (persistBatchSize > 1) {
transaction.setBatchMode(true);
transaction.setBatchSize(persistBatchSize);
transaction.setGetGeneratedKeys(getGeneratedKeys);
} else {
// explicitly turn off JDBC batching in case
// is has been turned on globally
transaction.setBatchMode(false);
}
}
}
/**
* If we created a transaction commit it. We have successfully processed all
* the rows.
*/
protected void commitTransactionIfCreated() {
if (createdTransaction) {
transaction.commit();
}
}
/**
* Rollback the transaction if we where not successful in processing all the
* rows.
*/
protected void rollbackTransactionIfCreated(Throwable e) {
if (createdTransaction) {
transaction.rollback(e);
}
}
}
@@ -1,4 +1,4 @@
package io.ebeaninternal.server.text.csv;
package io.ebean.csv.reader;
import io.ebean.text.StringParser;
@@ -0,0 +1,6 @@
module io.ebean.csv.reader {
requires io.ebean.api;
exports io.ebean.csv.reader;
}