package org.apache.ddlutils.task; | |
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you 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. | |
*/ | |
import java.io.File; | |
import java.util.ArrayList; | |
import java.util.Iterator; | |
import org.apache.ddlutils.Platform; | |
import org.apache.ddlutils.io.DataReader; | |
import org.apache.ddlutils.model.Database; | |
import org.apache.tools.ant.BuildException; | |
import org.apache.tools.ant.DirectoryScanner; | |
import org.apache.tools.ant.Task; | |
import org.apache.tools.ant.types.FileSet; | |
/** | |
* Inserts the data defined by the data XML file(s) into the database. This requires the schema | |
* in the database to match the schema defined by the XML files specified at the enclosing task.<br/> | |
* DdlUtils will honor the order imposed by the foreign keys. Ie. first all required entries are | |
* inserted, then the dependent ones. Obviously this requires that no circular references exist | |
* in the schema (DdlUtils currently does not check this). Also, the referenced entries must be | |
* present in the data, otherwise the task will fail. This behavior can be turned off via the | |
* <code>ensureForeignKeyOrder</code> attribute.<br/> | |
* In order to define data for foreign key dependencies that use auto-incrementing primary keys, | |
* simply use some unique values for their columns. DdlUtils then will automatically use the real | |
* primary key values when inserting the data. Note though that not every database supports the | |
* retrieval of auto-increment values which is necessary for this to work. | |
* | |
* @version $Revision: 289996 $ | |
* @ant.task name="writeDataToDatabase" | |
*/ | |
public class WriteDataToDatabaseCommand extends ConvertingDatabaseCommand | |
{ | |
/** A single data file to insert. */ | |
private File _singleDataFile = null; | |
/** The input files. */ | |
private ArrayList _fileSets = new ArrayList(); | |
/** Whether explicit values for identity columns will be used. */ | |
private boolean _useExplicitIdentityValues; | |
/** | |
* Defines whether values for identity columns in the data XML shall be used instead of | |
* letting the database define the value. Unless <code>ensureForeignKeyOrder</code> is | |
* set to false, setting this to <code>false</code> (the default) does not affect foreign | |
* keys as DdlUtils will automatically update the values of the columns of foreign keys | |
* pointing to the inserted row with the database-created values. | |
* | |
* @param useExplicitIdentityValues <code>true</code> if explicitly specified identity | |
* column values should be inserted instead of letting | |
* the database define the values for these columns | |
* @ant.not-required Default is <code>false</code> | |
*/ | |
public void setUseExplicitIdentityValues(boolean useExplicitIdentityValues) | |
{ | |
_useExplicitIdentityValues = useExplicitIdentityValues; | |
} | |
/** | |
* Adds a fileset. | |
* | |
* @param fileset The additional input files | |
*/ | |
public void addConfiguredFileset(FileSet fileset) | |
{ | |
_fileSets.add(fileset); | |
} | |
/** | |
* Specifies the name of the single XML file that contains the data to insert into the database. | |
* | |
* @param dataFile The data file | |
* @ant.not-required Use either this or <code>fileset</code> sub elements. | |
*/ | |
public void setDataFile(File dataFile) | |
{ | |
_singleDataFile = dataFile; | |
} | |
/** | |
* The maximum number of insert statements to combine in one batch. The number typically | |
* depends on the JDBC driver and the amount of available memory.<br/> | |
* This value is only used if <code>useBatchMode</code> is <code>true</code>. | |
* | |
* @param batchSize The number of objects | |
* @ant.not-required The default value is 1. | |
*/ | |
public void setBatchSize(int batchSize) | |
{ | |
getDataIO().setBatchSize(new Integer(batchSize)); | |
} | |
/** | |
* Specifies whether batch mode shall be used for inserting the data. In batch mode, insert statements | |
* for the same table are bundled together and executed as one statement. This can be a lot faster | |
* than single insert statements but is not supported by all JDBC drivers/databases. To achieve the | |
* highest performance, you should group the data in the XML file according to the tables. This is | |
* because a batch insert only works for one table at a time. Thus when the table changes in an | |
* entry in the XML file, the batch is committed and then a new one is started. | |
* | |
* @param useBatchMode <code>true</code> if batch mode shall be used | |
* @ant.not-required Per default batch mode is not used. | |
*/ | |
public void setUseBatchMode(boolean useBatchMode) | |
{ | |
getDataIO().setUseBatchMode(useBatchMode); | |
} | |
/** | |
* Specifies whether the foreign key order shall be honored when inserting data into the database. | |
* If not, DdlUtils will simply assume that the entry order is correct, i.e. that referenced rows | |
* come before referencing rows in the data XML. Note that execution will be slower when DdlUtils | |
* has to ensure the foreign-key order of the data. Thus if you know that the data is specified in | |
* foreign key order turn this off. | |
* | |
* @param ensureFKOrder <code>true</code> if the foreign key order shall be followed | |
* @ant.not-required Per default foreign key order is honored. | |
*/ | |
public void setEnsureForeignKeyOrder(boolean ensureFKOrder) | |
{ | |
getDataIO().setEnsureFKOrder(ensureFKOrder); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public void execute(DatabaseTaskBase task, Database model) throws BuildException | |
{ | |
if ((_singleDataFile != null) && !_fileSets.isEmpty()) | |
{ | |
throw new BuildException("Please use either the datafile attribute or the sub fileset element, but not both"); | |
} | |
Platform platform = getPlatform(); | |
DataReader dataReader = null; | |
platform.setIdentityOverrideOn(_useExplicitIdentityValues); | |
try | |
{ | |
dataReader = getDataIO().getConfiguredDataReader(platform, model); | |
dataReader.getSink().start(); | |
if (_singleDataFile != null) | |
{ | |
readSingleDataFile(task, dataReader, _singleDataFile); | |
} | |
else | |
{ | |
for (Iterator it = _fileSets.iterator(); it.hasNext();) | |
{ | |
FileSet fileSet = (FileSet)it.next(); | |
File fileSetDir = fileSet.getDir(task.getProject()); | |
DirectoryScanner scanner = fileSet.getDirectoryScanner(task.getProject()); | |
String[] files = scanner.getIncludedFiles(); | |
for (int idx = 0; (files != null) && (idx < files.length); idx++) | |
{ | |
readSingleDataFile(task, dataReader, new File(fileSetDir, files[idx])); | |
} | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
handleException(ex, ex.getMessage()); | |
} | |
finally | |
{ | |
if (dataReader != null) | |
{ | |
dataReader.getSink().end(); | |
} | |
} | |
} | |
/** | |
* Reads a single data file. | |
* | |
* @param task The parent task | |
* @param reader The data reader | |
* @param dataFile The schema file | |
*/ | |
private void readSingleDataFile(Task task, DataReader reader, File dataFile) throws BuildException | |
{ | |
if (!dataFile.exists()) | |
{ | |
_log.error("Could not find data file " + dataFile.getAbsolutePath()); | |
} | |
else if (!dataFile.isFile()) | |
{ | |
_log.error("Path " + dataFile.getAbsolutePath() + " does not denote a data file"); | |
} | |
else if (!dataFile.canRead()) | |
{ | |
_log.error("Could not read data file " + dataFile.getAbsolutePath()); | |
} | |
else | |
{ | |
try | |
{ | |
getDataIO().writeDataToDatabase(reader, dataFile.getAbsolutePath()); | |
_log.info("Written data from file " + dataFile.getAbsolutePath() + " to database"); | |
} | |
catch (Exception ex) | |
{ | |
handleException(ex, "Could not parse or write data file " + dataFile.getAbsolutePath()); | |
} | |
} | |
} | |
} |