DataRepoGenerator.java
/*
* Copyright 2013 Gregory Graham.
*
* 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.
*/
package nz.co.gregs.dbvolution.generation;
import nz.co.gregs.dbvolution.databases.metadata.ForeignKeyRecognisor;
import nz.co.gregs.dbvolution.databases.metadata.Options;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nz.co.gregs.dbvolution.databases.DBDatabase;
import nz.co.gregs.dbvolution.databases.metadata.DBDatabaseMetaData;
import nz.co.gregs.dbvolution.databases.metadata.TableMetaData;
import nz.co.gregs.dbvolution.datatypes.DBUnknownDatatype;
import nz.co.gregs.dbvolution.exceptions.NoAvailableDatabaseException;
import nz.co.gregs.dbvolution.exceptions.UnknownJavaSQLTypeException;
/**
* Automatically generates Java files to be used in your data model.
*
* <p>
* DBvolution data model classes (DBRow subclasses) are designed to be easy to
* create and modify. However with a complex existing database it can be easier
* to use this class to generate the data model and then add the details.
*
* @author Gregory Graham
*/
class DataRepoGenerator {
private DataRepoGenerator() {
}
/**
*
* Creates DBTableRow classes corresponding to all the tables and views
* accessible to the user specified in the database supplied.
*
* <p>
* Classes are placed in the correct subdirectory of the base directory as
* defined by the package name supplied.
*
* <p>
* Convenience method which calls {@code generateClasses(jdbcURL, username,
* password, packageName, 1L, baseDirectory,new PrimaryKeyRecognisor(),new
* ForeignKeyRecognisor());}
*
* 1 Database exceptions may be thrown
*
* @param database database
* @param packageName packageName
* @param baseDirectory baseDirectory
* @return
* @throws java.sql.SQLException java.sql.SQLException
* @throws java.io.FileNotFoundException java.io.FileNotFoundException
* @throws java.io.IOException java.io.IOException
*/
static public DataRepo generateClasses(DBDatabase database, String packageName) throws SQLException, FileNotFoundException, IOException {
return generateClasses(database, packageName, new Options());
}
/**
*
* Creates DBTableRow classes corresponding to all the tables and views
* accessible to the user specified in the database supplied.
*
* <p>
* Classes are placed in the correct subdirectory of the base directory as
* defined by the package name supplied.
*
* <p>
* Convenience method which calls {@code
* generateClasses(jdbcURL,username,password,packageName,baseDirectory,new
* PrimaryKeyRecognisor(),new ForeignKeyRecognisor());}
*
* @param database database
* @param versionNumber - the value to use for serialVersionUID
*
* 1 Database exceptions may be thrown
* @param packageName packageName
* @param baseDirectory baseDirectory
* @return
* @throws java.sql.SQLException java.sql.SQLException
* @throws java.io.FileNotFoundException java.io.FileNotFoundException
* @throws java.io.IOException java.io.IOException
*
*
*/
static public DataRepo generateClasses(DBDatabase database, String packageName, Long versionNumber) throws SQLException, FileNotFoundException, IOException {
Options opts = Options.empty()
.setVersionNumber(versionNumber)
.setDBDatabase(database)
.setPackageName(packageName);
return generateClasses(opts);
}
/**
*
* Creates DBTableRow classes corresponding to all the tables and views
* accessible to the user specified in the database supplied.
*
* <p>
* Classes are placed in the correct subdirectory of the base directory as
* defined by the package name supplied.
*
* <p>
* Primary keys and foreign keys are created based on the definitions within
* the database and the results from the PK and FK recognisors.
*
* <p>
* Database exceptions may be thrown
*
* @param database database
* @param packageName packageName
* @param options preferences for the class generation
* @return
* @throws java.sql.SQLException java.sql.SQLException
* @throws java.io.FileNotFoundException java.io.FileNotFoundException
* @throws java.io.IOException java.io.IOException
*/
static public DataRepo generateClasses(DBDatabase database, String packageName, Options options) throws SQLException, FileNotFoundException, IOException {
options.setDBDatabase(database);
options.setPackageName(packageName);
return generateClasses(options);
}
static private DataRepo generateClasses(Options options) throws SQLException, FileNotFoundException, IOException {
DBDatabase database = options.getDBDatabase();
String packageName = options.getPackageName();
DataRepo repo = new DataRepo(options);
String viewsPackage = packageName + ".views";
DataRepo parsedViews = parseViews(database, viewsPackage, options);
repo.addViews(parsedViews.getTables());
String tablesPackage = packageName + ".tables";
DataRepo parsedTables = parseTables(database, tablesPackage, options);
repo.addTables(parsedTables.getTables());
repo.compile(options);
return repo;
}
/**
* Generate the required Java classes for all the Tables on the database.
*
* <p>
* Connects to the database using the DBDatabase instance supplied and
* generates class for the tables it can find.
*
* <p>
* Classes will be in the package supplied, serialVersionUID will be set to
* the version number supplied and the supplied {@link PrimaryKeyRecognisor}
* and {@link ForeignKeyRecognisor} will be used.
*
*
* @param database database
* @param packageName packageName
* @param options
*
*
* @return a List of DBTableClass instances representing the tables found on
* the database 1 Database exceptions may be thrown
* @throws java.sql.SQLException java.sql.SQLException
*/
private static DataRepo parseTables(DBDatabase database, String packageName, Options options) throws SQLException {
Options opts = options.copy();
opts.setDBDatabase(database);
opts.setPackageName(packageName);
opts.setObjectTypes("TABLE");
return parseObjectTypes(opts);
}
/**
* Generate the required Java classes for all the Views on the database.
*
* <p>
* Connects to the database using the DBDatabase instance supplied and
* generates class for the views it can find.
*
* <p>
* Classes will be in the package supplied, serialVersionUID will be set to
* the version number supplied and the supplied {@link PrimaryKeyRecognisor}
* and {@link ForeignKeyRecognisor} will be used.
*
* @param database database
* @param packageName packageName
* @param options
*
* @return a List of DBTableClass instances representing the views found on
* the database 1 Database exceptions may be thrown
* @throws java.sql.SQLException java.sql.SQLException
*/
private static DataRepo parseViews(DBDatabase database, String packageName, Options originalOptions) throws SQLException {
Options options = originalOptions.copy();
options.setDBDatabase(database);
options.setPackageName(packageName);
options.setObjectTypes("VIEW");
final DataRepo dataRepo = new DataRepo(options);
dataRepo.addViews(parseObjectTypes(options).getTables());
return dataRepo;
}
/**
* Generate the required Java classes for all the Tables and Views on the
* database.
*
* <p>
* Connects to the database using the DBDatabase instance supplied and
* generates class for the tables and views it can find.
*
* <p>
* Classes will be in the package supplied, serialVersionUID will be set to
* the version number supplied and the supplied {@link PrimaryKeyRecognisor}
* and {@link ForeignKeyRecognisor} will be used.
*
*
*
*
* @return a List of DBTableClass instances representing the tables and views
* found on the database 1 Database exceptions may be thrown
*/
private static DataRepo parseObjectTypes(Options options) throws SQLException {
// Options opts = Options
// .copy(options)
// .setDBDatabase(db)
// .setPackageName(packageName)
// .setObjectTypes(dbObjectTypes);
DataRepo datarepo = getDataRepo(options);
return datarepo;
}
public static DataRepo getDataRepo(Options opts) throws SQLException, NoAvailableDatabaseException {
DataRepo datarepo = new DataRepo(opts);
DBDatabase db = opts.getDBDatabase();
if (db != null) {
DBDatabaseMetaData metaData = db.getDBDatabaseMetaData(opts);
String catalog = metaData.getCatalog(); // this is reporting dbvtest (the schema)
String schema = metaData.getSchema(); // and this is null
List<TableMetaData> tables = metaData.getTables();
for (TableMetaData table : tables) {
final String tableName = table.getTableName();
if (schema == null) {
schema = table.getSchema();
}
final String className = Utility.toClassCase(tableName);
DBTableClass dbTableClass = new DBTableClass(tableName, schema, opts.getPackageName(), className);
datarepo.addTable(dbTableClass);
List<TableMetaData.PrimaryKey> primaryKeys = table.getPrimaryKeys();
List<String> pkNames = new ArrayList<String>();
for (TableMetaData.PrimaryKey primaryKey : primaryKeys) {
pkNames.add(primaryKey.getName());
}
String classTableName = dbTableClass.getTableName();
List<TableMetaData.ForeignKey> foreignKeys = table.getForeignKeys(catalog, schema, classTableName);
Map<String, TableMetaData.ForeignKey> fkNames = new HashMap<>();
foreignKeys.stream()
.forEach((fk) -> {
fkNames.put(fk.getName(), fk);
});
List<TableMetaData.Column> columns = table.getColumns();
for (TableMetaData.Column col : columns) {
DBTableField dbTableField = new DBTableField();
dbTableClass.getFields().add(dbTableField);
dbTableField.columnName = col.getColumnName();
dbTableField.fieldName = Utility.toFieldCase(dbTableField.columnName);
dbTableField.referencedTable = col.getReferencedTable();
dbTableField.precision = col.getColumnSize();
dbTableField.comments = col.getRemarks();
dbTableField.isAutoIncrement = col.getIsAutoIncrement();
try {
dbTableField.sqlDataTypeInt = col.getDatatype();
dbTableField.sqlDataTypeName = col.getTypeName();
dbTableField.columnType = Utility.getQDTClassOfSQLType(db, dbTableField.sqlDataTypeName, dbTableField.sqlDataTypeInt, dbTableField.precision, opts.getTrimCharColumns());
} catch (UnknownJavaSQLTypeException ex) {
dbTableField.columnType = DBUnknownDatatype.class;
dbTableField.javaSQLDatatype = ex.getUnknownJavaSQLType();
}
if (pkNames.contains(dbTableField.columnName)
|| (opts.getPkRecog()!=null &&opts.getPkRecog().isPrimaryKeyColumn(classTableName, dbTableField.columnName))
) {
dbTableField.isPrimaryKey = true;
}
TableMetaData.ForeignKey fk = fkNames.get(dbTableField.columnName);
ForeignKeyRecognisor fkRecog = opts.getFkRecog();
if (fk != null) {
dbTableField.isForeignKey = true;
dbTableField.referencesClass = Utility.toClassCase(fk.getPrimaryKeyTableName());
dbTableField.referencesField = fk.getPrimaryKeyColumnName();
} else if (fkRecog != null && fkRecog.isForeignKeyColumn(classTableName, dbTableField.columnName)) {
dbTableField.isForeignKey = true;
dbTableField.referencesField = fkRecog.getReferencedColumn(classTableName, dbTableField.columnName);
String fkTable = fkRecog.getReferencedTable(classTableName, dbTableField.columnName);
dbTableField.referencesClass = Utility.toClassCase(fkTable);
}
}
}
}
return datarepo;
}
}