com.querydsl.sql.codegen.MetaDataExporter Maven / Gradle / Ivy
/*
* Copyright 2015, The Querydsl Team (http://www.querydsl.com/team)
*
* 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 com.querydsl.sql.codegen;
import com.querydsl.codegen.*;
import com.querydsl.codegen.utils.CodeWriter;
import com.querydsl.codegen.utils.JavaWriter;
import com.querydsl.codegen.utils.ScalaWriter;
import com.querydsl.codegen.utils.model.ClassType;
import com.querydsl.codegen.utils.model.SimpleType;
import com.querydsl.codegen.utils.model.Type;
import com.querydsl.codegen.utils.model.TypeCategory;
import com.querydsl.sql.*;
import com.querydsl.sql.codegen.support.*;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.logging.Logger;
/**
* {@code MetadataExporter} exports JDBC metadata to Querydsl query types
*
* Example
*
*
* MetaDataExporter exporter = new MetaDataExporter();
* exporter.setPackageName("com.example.domain");
* exporter.setTargetFolder(new File("target/generated-sources/java"));
* exporter.export(connection.getMetaData());
*
*
* @author tiwe
*/
public class MetaDataExporter {
private static final Logger logger = Logger.getLogger(MetaDataExporter.class.getName());
private final SQLTemplatesRegistry sqlTemplatesRegistry = new SQLTemplatesRegistry();
private final SQLCodegenModule module = new SQLCodegenModule();
private final Set classes = new HashSet();
private final Map entityToWrapped = new HashMap();
private final MetadataExporterConfig config;
private String beanPackageName;
private Serializer serializer;
private QueryTypeFactory queryTypeFactory;
private NamingStrategy namingStrategy;
private Configuration configuration;
private KeyDataFactory keyDataFactory;
private Serializer beanSerializer;
private TypeMappings typeMappings;
public MetaDataExporter(MetadataExporterConfig config) {
this.config = config;
}
protected EntityType createEntityType(SchemaAndTable schemaAndTable, final String className) {
EntityType classModel;
if (beanSerializer == null) {
String packageName = normalizePackage(module.getPackageName(), schemaAndTable);
String simpleName = module.getPrefix() + className + module.getSuffix();
Type classTypeModel =
new SimpleType(
TypeCategory.ENTITY,
packageName + "." + simpleName,
packageName,
simpleName,
false,
false);
classModel =
new EntityType(
classTypeModel,
module.get(Function.class, CodegenModule.VARIABLE_NAME_FUNCTION_CLASS));
typeMappings.register(classModel, classModel);
} else {
String beanPackage = normalizePackage(beanPackageName, schemaAndTable);
String simpleName = module.getBeanPrefix() + className + module.getBeanSuffix();
Type classTypeModel =
new SimpleType(
TypeCategory.ENTITY,
beanPackage + "." + simpleName,
beanPackage,
simpleName,
false,
false);
classModel =
new EntityType(
classTypeModel,
module.get(Function.class, CodegenModule.VARIABLE_NAME_FUNCTION_CLASS));
Type mappedType = queryTypeFactory.create(classModel);
entityToWrapped.put(classModel, mappedType);
typeMappings.register(classModel, mappedType);
}
classModel.getData().put("schema", schemaAndTable.getSchema());
classModel.getData().put("table", schemaAndTable.getTable());
return classModel;
}
private String normalizePackage(String packageName, SchemaAndTable schemaAndTable) {
String rval = packageName;
if (config.isSchemaToPackage()) {
rval = namingStrategy.getPackage(rval, schemaAndTable);
}
return rval;
}
protected Property createProperty(
EntityType classModel, String normalizedColumnName, String propertyName, Type typeModel) {
return new Property(
classModel, propertyName, propertyName, typeModel, Collections.emptyList(), false);
}
/**
* Export the tables based on the given database metadata
*
* @param md database metadata
* @throws SQLException
*/
public void export(DatabaseMetaData md) throws SQLException {
configuration = module.get(Configuration.class);
configureModule();
if (config.getNamingStrategyClass() != null) {
try {
namingStrategy = config.getNamingStrategyClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} else {
namingStrategy = new DefaultNamingStrategy();
}
module.bind(NamingStrategy.class, namingStrategy);
if (config.getBeanPackageName() == null) {
beanPackageName = module.getPackageName();
} else {
beanPackageName = config.getBeanPackageName();
}
module.bind(SQLCodegenModule.BEAN_PACKAGE_NAME, beanPackageName);
module.loadExtensions();
classes.clear();
typeMappings = module.get(TypeMappings.class);
queryTypeFactory = module.get(QueryTypeFactory.class);
serializer = module.get(Serializer.class);
beanSerializer = module.get(Serializer.class, SQLCodegenModule.BEAN_SERIALIZER);
namingStrategy = module.get(NamingStrategy.class);
SQLTemplates templates = sqlTemplatesRegistry.getTemplates(md);
if (templates != null) {
configuration.setTemplates(templates);
} else {
logger.info("Found no specific dialect for " + md.getDatabaseProductName());
}
if (beanSerializer == null) {
keyDataFactory =
new KeyDataFactory(
namingStrategy,
module.getPackageName(),
module.getPrefix(),
module.getSuffix(),
config.isSchemaToPackage());
} else {
keyDataFactory =
new KeyDataFactory(
namingStrategy,
beanPackageName,
module.getBeanPrefix(),
module.getBeanSuffix(),
config.isSchemaToPackage());
}
String[] typesArray = null;
if (config.getTableTypesToExport() != null && !config.getTableTypesToExport().isEmpty()) {
List types = new ArrayList();
for (String tableType : config.getTableTypesToExport().split(",")) {
types.add(tableType.trim());
}
typesArray = types.toArray(new String[0]);
} else if (!config.isExportAll()) {
List types = new ArrayList(2);
if (config.isExportTables()) {
types.add("TABLE");
}
if (config.isExportViews()) {
types.add("VIEW");
}
typesArray = types.toArray(new String[0]);
}
List catalogs = patternAsList(config.getCatalogPattern());
List schemas = patternAsList(config.getSchemaPattern());
List tables = patternAsList(config.getTableNamePattern());
for (String catalog : catalogs) {
catalog = trimIfNonNull(catalog);
for (String schema : schemas) {
schema = trimIfNonNull(schema);
for (String table : tables) {
table = trimIfNonNull(table);
handleTables(md, catalog, schema, table, typesArray);
}
}
}
}
private void configureModule() {
if (config.getNamePrefix() != null) module.bind(CodegenModule.PREFIX, config.getNamePrefix());
if (config.getNameSuffix() != null) {
module.bind(CodegenModule.SUFFIX, config.getNameSuffix());
}
if (config.getBeanPrefix() != null) {
module.bind(SQLCodegenModule.BEAN_PREFIX, config.getBeanPrefix());
}
if (config.getBeanSuffix() != null) {
module.bind(SQLCodegenModule.BEAN_SUFFIX, config.getBeanSuffix());
}
module.bind(SQLCodegenModule.PACKAGE_NAME, config.getPackageName());
module.bind(SQLCodegenModule.INNER_CLASSES_FOR_KEYS, config.isInnerClassesForKeys());
module.bind(NamingStrategy.class, namingStrategy);
module.bind(SQLCodegenModule.SCHEMA_TO_PACKAGE, config.isSchemaToPackage());
if (config.getImports() != null && !config.getImports().isEmpty())
module.bind(CodegenModule.IMPORTS, new HashSet(config.getImports()));
module.bindInstance(
CodegenModule.GENERATED_ANNOTATION_CLASS,
GeneratedAnnotationResolver.resolve(config.getGeneratedAnnotationClass()));
if (config.isExportBeans()) {
Serializer serializer;
if (config.getBeanSerializerClass() == null) {
serializer = new BeanSerializer();
} else {
try {
serializer = config.getBeanSerializerClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
if (serializer instanceof BeanSerializer bserializer) {
if (config.getBeanInterfaces() != null) {
for (String iface : config.getBeanInterfaces()) {
int sepIndex = iface.lastIndexOf('.');
if (sepIndex < 0) {
bserializer.addInterface(new SimpleType(iface));
} else {
String packageName = iface.substring(0, sepIndex);
String simpleName = iface.substring(sepIndex + 1);
bserializer.addInterface(new SimpleType(iface, packageName, simpleName));
}
}
}
bserializer.setAddFullConstructor(config.isBeanAddFullConstructor());
bserializer.setAddToString(config.isBeanAddToString());
bserializer.setPrintSupertype(config.isBeanPrintSupertype());
}
module.bind(SQLCodegenModule.BEAN_SERIALIZER, serializer);
}
if (config.getCustomTypes() != null) {
for (CustomType cl : config.getCustomTypes()) {
try {
configuration.register(
(com.querydsl.sql.types.Type) Class.forName(cl.getClassName()).newInstance());
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
if (config.getTypeMappings() != null) {
for (TypeMapping mapping : config.getTypeMappings()) {
mapping.apply(configuration);
}
}
if (config.getNumericMappings() != null) {
for (NumericMapping mapping : config.getNumericMappings()) {
mapping.apply(configuration);
}
}
if (config.getRenameMappings() != null) {
for (RenameMapping mapping : config.getRenameMappings()) {
mapping.apply(configuration);
}
}
if (config.getColumnComparatorClass() != null)
module.bind(
SQLCodegenModule.COLUMN_COMPARATOR,
config.getColumnComparatorClass().asSubclass(Comparator.class));
if (config.getSerializerClass() != null) {
module.bind(Serializer.class, config.getSerializerClass());
}
}
private String trimIfNonNull(String input) {
return input != null ? input.trim() : null;
}
/**
* Splits the input on ',' if non-null and a ',' is present. Returns a singletonList of null if
* null
*/
private List patternAsList(String input) {
if (input != null && input.contains(",")) {
return Arrays.asList(input.split(","));
} else {
return Collections.singletonList(input);
}
}
private void handleTables(
DatabaseMetaData md,
String catalogPattern,
String schemaPattern,
String tablePattern,
String[] types)
throws SQLException {
try (ResultSet tables = md.getTables(catalogPattern, schemaPattern, tablePattern, types)) {
while (tables.next()) {
handleTable(md, tables);
}
}
}
Set getClasses() {
return classes;
}
private void handleColumn(EntityType classModel, String tableName, ResultSet columns)
throws SQLException {
String columnName = normalize(columns.getString("COLUMN_NAME"));
String normalizedColumnName = namingStrategy.normalizeColumnName(columnName);
int columnType = columns.getInt("DATA_TYPE");
String typeName = columns.getString("TYPE_NAME");
Number columnSize = (Number) columns.getObject("COLUMN_SIZE");
Number columnDigits = (Number) columns.getObject("DECIMAL_DIGITS");
String columnDefaultValue = columns.getString("COLUMN_DEF");
int columnIndex = columns.getInt("ORDINAL_POSITION");
int nullable = columns.getInt("NULLABLE");
String propertyName = namingStrategy.getPropertyName(normalizedColumnName, classModel);
Class> clazz =
configuration.getJavaType(
columnType,
typeName,
columnSize != null ? columnSize.intValue() : 0,
columnDigits != null ? columnDigits.intValue() : 0,
tableName,
columnName);
if (clazz == null) {
clazz = Object.class;
}
TypeCategory fieldType = TypeCategory.get(clazz.getName());
if (Number.class.isAssignableFrom(clazz)) {
fieldType = TypeCategory.NUMERIC;
} else if (Enum.class.isAssignableFrom(clazz)) {
fieldType = TypeCategory.ENUM;
}
Type typeModel = new ClassType(fieldType, clazz);
Property property = createProperty(classModel, normalizedColumnName, propertyName, typeModel);
ColumnMetadata column =
ColumnMetadata.named(normalizedColumnName).ofType(columnType).withIndex(columnIndex);
if (nullable == DatabaseMetaData.columnNoNulls) {
column = column.notNull();
}
if (columnSize != null) {
column = column.withSize(columnSize.intValue());
}
if (columnDigits != null) {
column = column.withDigits(columnDigits.intValue());
}
property.getData().put("COLUMN", column);
if (config.isColumnAnnotations()) {
property.addAnnotation(new ColumnImpl(normalizedColumnName));
}
if (config.isValidationAnnotations()) {
if (nullable == DatabaseMetaData.columnNoNulls && columnDefaultValue == null) {
property.addAnnotation(new NotNullImpl());
}
int size = columns.getInt("COLUMN_SIZE");
if (size > 0 && clazz.equals(String.class)) {
property.addAnnotation(new SizeImpl(0, size));
}
}
classModel.addProperty(property);
}
private void handleTable(DatabaseMetaData md, ResultSet tables) throws SQLException {
String catalog = tables.getString("TABLE_CAT");
String schema = tables.getString("TABLE_SCHEM");
String schemaName = normalize(tables.getString("TABLE_SCHEM"));
String tableName = normalize(tables.getString("TABLE_NAME"));
String normalizedSchemaName = namingStrategy.normalizeSchemaName(schemaName);
String normalizedTableName = namingStrategy.normalizeTableName(tableName);
SchemaAndTable schemaAndTable = new SchemaAndTable(normalizedSchemaName, normalizedTableName);
if (!namingStrategy.shouldGenerateClass(schemaAndTable)) {
return;
}
String className = namingStrategy.getClassName(schemaAndTable);
EntityType classModel = createEntityType(schemaAndTable, className);
if (config.isExportPrimaryKeys()) {
// collect primary keys
Map primaryKeyData =
keyDataFactory.getPrimaryKeys(md, catalog, schema, tableName);
if (!primaryKeyData.isEmpty()) {
classModel.getData().put(PrimaryKeyData.class, primaryKeyData.values());
}
}
if (config.isExportForeignKeys()) {
if (config.isExportDirectForeignKeys()) {
// collect foreign keys
Map foreignKeyData =
keyDataFactory.getImportedKeys(md, catalog, schema, tableName);
if (!foreignKeyData.isEmpty()) {
Collection foreignKeysToGenerate = new LinkedHashSet();
for (ForeignKeyData fkd : foreignKeyData.values()) {
if (namingStrategy.shouldGenerateForeignKey(schemaAndTable, fkd)) {
foreignKeysToGenerate.add(fkd);
}
}
if (!foreignKeysToGenerate.isEmpty()) {
classModel.getData().put(ForeignKeyData.class, foreignKeysToGenerate);
}
}
}
if (config.isExportInverseForeignKeys()) {
// collect inverse foreign keys
Map inverseForeignKeyData =
keyDataFactory.getExportedKeys(md, catalog, schema, tableName);
if (!inverseForeignKeyData.isEmpty()) {
classModel.getData().put(InverseForeignKeyData.class, inverseForeignKeyData.values());
}
}
}
// collect columns
try (ResultSet columns = md.getColumns(catalog, schema, tableName.replace("/", "//"), null)) {
while (columns.next()) {
handleColumn(classModel, tableName, columns);
}
}
// serialize model
serialize(classModel, schemaAndTable);
logger.info("Exported " + tableName + " successfully");
}
private String normalize(String str) {
if (config.isLowerCase() && str != null) {
return str.toLowerCase();
} else {
return str;
}
}
private void serialize(EntityType type, SchemaAndTable schemaAndTable) {
try {
String fileSuffix = config.isCreateScalaSources() ? ".scala" : ".java";
if (beanSerializer != null) {
String packageName = normalizePackage(beanPackageName, schemaAndTable);
String path = packageName.replace('.', '/') + "/" + type.getSimpleName() + fileSuffix;
write(
beanSerializer,
new File(
config.getBeansTargetFolder() != null
? config.getBeansTargetFolder()
: config.getTargetFolder(),
path),
type);
String otherPath = entityToWrapped.get(type).getFullName().replace('.', '/') + fileSuffix;
write(serializer, new File(config.getTargetFolder(), otherPath), type);
} else {
String packageName = normalizePackage(module.getPackageName(), schemaAndTable);
String path = packageName.replace('.', '/') + "/" + type.getSimpleName() + fileSuffix;
write(serializer, new File(config.getTargetFolder(), path), type);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void write(Serializer serializer, File targetFile, EntityType type) throws IOException {
if (!classes.add(targetFile.getPath())) {
throw new IllegalStateException(
"Attempted to write multiple times to "
+ targetFile.getPath()
+ ", please check your configuration");
}
StringWriter w = new StringWriter();
CodeWriter writer = config.isCreateScalaSources() ? new ScalaWriter(w) : new JavaWriter(w);
serializer.serialize(type, SimpleSerializerConfig.DEFAULT, writer);
// conditional creation
boolean generate = true;
byte[] bytes = w.toString().getBytes(config.getSourceEncoding());
if (targetFile.exists() && targetFile.length() == bytes.length) {
String str = new String(Files.readAllBytes(targetFile.toPath()), config.getSourceEncoding());
if (str.equals(w.toString())) {
generate = false;
}
} else {
targetFile.getParentFile().mkdirs();
}
if (generate) {
Files.write(targetFile.toPath(), bytes);
}
}
public void setConfiguration(Configuration configuration) {
module.bind(Configuration.class, configuration);
}
/**
* Set the type mappings to use
*
* @param typeMappings
*/
public void setTypeMappings(TypeMappings typeMappings) {
module.bind(TypeMappings.class, typeMappings);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy