org.databene.platform.db.DBSystem Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of databene-benerator Show documentation
Show all versions of databene-benerator Show documentation
benerator is a framework for creating realistic and valid high-volume test data, used for
testing (unit/integration/load) and showcase setup.
Metadata constraints are imported from systems and/or configuration files. Data can imported from
and exported to files and systems, anonymized or generated from scratch. Domain packages provide
reusable generators for creating domain-specific data as names and addresses internationalizable
in language and region. It is strongly customizable with plugins and configuration options.
The newest version!
/*
* (c) Copyright 2007-2014 by Volker Bergmann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted under the terms of the
* GNU General Public License (GPL).
*
* For redistributing this software or a derivative work under a license other
* than the GPL-compatible Free Software License as defined by the Free
* Software Foundation or approved by OSI, you must first obtain a commercial
* license to this software product from Volker Bergmann.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
* REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
* HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.databene.platform.db;
import java.io.File;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.databene.benerator.Consumer;
import org.databene.benerator.storage.AbstractStorageSystem;
import org.databene.benerator.storage.StorageSystemInserter;
import org.databene.commons.ArrayFormat;
import org.databene.commons.CollectionUtil;
import org.databene.commons.ConfigurationError;
import org.databene.commons.ConnectFailedException;
import org.databene.commons.Context;
import org.databene.commons.IOUtil;
import org.databene.commons.ImportFailedException;
import org.databene.commons.ObjectNotFoundException;
import org.databene.commons.StringUtil;
import org.databene.commons.collection.OrderedNameMap;
import org.databene.commons.converter.AnyConverter;
import org.databene.commons.version.VersionNumber;
import org.databene.formats.DataSource;
import org.databene.formats.util.ConvertingDataSource;
import org.databene.jdbacl.ColumnInfo;
import org.databene.jdbacl.DBUtil;
import org.databene.jdbacl.DatabaseDialect;
import org.databene.jdbacl.DatabaseDialectManager;
import org.databene.jdbacl.JDBCConnectData;
import org.databene.jdbacl.ResultSetConverter;
import org.databene.jdbacl.dialect.OracleDialect;
import org.databene.jdbacl.model.DBCatalog;
import org.databene.jdbacl.model.DBColumn;
import org.databene.jdbacl.model.DBDataType;
import org.databene.jdbacl.model.DBForeignKeyConstraint;
import org.databene.jdbacl.model.DBMetaDataImporter;
import org.databene.jdbacl.model.DBPrimaryKeyConstraint;
import org.databene.jdbacl.model.DBSchema;
import org.databene.jdbacl.model.DBTable;
import org.databene.jdbacl.model.DBUniqueConstraint;
import org.databene.jdbacl.model.Database;
import org.databene.jdbacl.model.cache.CachingDBImporter;
import org.databene.jdbacl.model.jdbc.JDBCDBImporter;
import org.databene.jdbacl.model.jdbc.JDBCMetaDataUtil;
import org.databene.model.data.ComplexTypeDescriptor;
import org.databene.model.data.ComponentDescriptor;
import org.databene.model.data.DataModel;
import org.databene.model.data.Entity;
import org.databene.model.data.IdDescriptor;
import org.databene.model.data.Mode;
import org.databene.model.data.PartDescriptor;
import org.databene.model.data.ReferenceDescriptor;
import org.databene.model.data.SimpleTypeDescriptor;
import org.databene.model.data.TypeDescriptor;
import org.databene.model.data.TypeMapper;
import org.databene.script.PrimitiveType;
import org.databene.script.expression.ConstantExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class that serves as parent for classes which connect to databases using JDBC.
* Created: 07.01.2013 08:11:25
* @since 0.8.0
* @author Volker Bergmann
*/
public abstract class DBSystem extends AbstractStorageSystem {
protected Logger logger = LoggerFactory.getLogger(getClass());
private static final int DEFAULT_FETCH_SIZE = 100;
private static final VersionNumber MIN_ORACLE_VERSION = VersionNumber.valueOf("10.2.0.4");
// constants -------------------------------------------------------------------------------------------------------
private static final TypeDescriptor[] EMPTY_TYPE_DESCRIPTOR_ARRAY = new TypeDescriptor[0];
// attributes ------------------------------------------------------------------------------------------------------
private String id;
private String environment;
private String url;
private String user;
private String password;
private String driver;
private String catalogName;
private String schemaName;
private String includeTables;
private String excludeTables;
private boolean metaDataCache;
protected boolean batch;
protected boolean readOnly;
private boolean lazy;
private boolean acceptUnknownColumnTypes;
private int fetchSize;
protected Database database;
protected DBMetaDataImporter importer;
private OrderedNameMap typeDescriptors;
protected Map tables;
private TypeMapper driverTypeMapper;
protected DatabaseDialect dialect;
private boolean dynamicQuerySupported;
private boolean connectedBefore;
private AtomicInteger invalidationCount;
// constructors ----------------------------------------------------------------------------------------------------
public DBSystem(String id, String url, String driver, String user, String password, DataModel dataModel) {
this(id, dataModel);
setUrl(url);
setUser(user);
setPassword(password);
setDriver(driver);
checkOracleDriverVersion(driver);
}
public DBSystem(String id, String environment, DataModel dataModel) {
this(id, dataModel);
setEnvironment(environment);
}
private DBSystem(String id, DataModel dataModel) {
setId(id);
setDataModel(dataModel);
setSchema(null);
setIncludeTables(".*");
setExcludeTables(null);
setFetchSize(DEFAULT_FETCH_SIZE);
setMetaDataCache(false);
setBatch(false);
setReadOnly(false);
setLazy(true);
setDynamicQuerySupported(true);
this.typeDescriptors = null;
this.driverTypeMapper = driverTypeMapper();
this.connectedBefore = false;
this.invalidationCount = new AtomicInteger();
}
// properties ------------------------------------------------------------------------------------------------------
@Override
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEnvironment() {
return (environment != null ? environment : user);
}
private void setEnvironment(String environment) {
if (StringUtil.isEmpty(environment)) {
this.environment = null;
return;
}
logger.debug("setting environment '{}'", environment);
JDBCConnectData connectData = DBUtil.getConnectData(environment);
this.environment = environment;
this.url = connectData.url;
this.driver = connectData.driver;
this.catalogName = connectData.catalog;
this.schemaName = connectData.schema;
this.user = connectData.user;
this.password = connectData.password;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = StringUtil.emptyToNull(password);
}
public String getCatalog() {
return catalogName;
}
public void setCatalog(String catalog) {
this.catalogName = catalog;
}
public String getSchema() {
return schemaName;
}
public void setSchema(String schema) {
this.schemaName = StringUtil.emptyToNull(StringUtil.trim(schema));
}
@Deprecated
public void setTableFilter(String tableFilter) {
setIncludeTables(tableFilter);
}
public String getIncludeTables() {
return includeTables;
}
public void setIncludeTables(String includeTables) {
this.includeTables = includeTables;
}
public String getExcludeTables() {
return excludeTables;
}
public void setExcludeTables(String excludeTables) {
this.excludeTables = excludeTables;
}
public boolean isMetaDataCache() {
return metaDataCache;
}
public void setMetaDataCache(boolean metaDataCache) {
this.metaDataCache = metaDataCache;
}
public boolean isBatch() {
return batch;
}
public void setBatch(boolean batch) {
this.batch = batch;
}
public int getFetchSize() {
return fetchSize;
}
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public boolean isLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
public void setDynamicQuerySupported(boolean dynamicQuerySupported) {
this.dynamicQuerySupported = dynamicQuerySupported;
}
public void setAcceptUnknownColumnTypes(boolean acceptUnknownColumnTypes) {
this.acceptUnknownColumnTypes = acceptUnknownColumnTypes;
}
// DescriptorProvider interface ------------------------------------------------------------------------------------
@Override
public TypeDescriptor[] getTypeDescriptors() {
logger.debug("getTypeDescriptors()");
parseMetadataIfNecessary();
if (typeDescriptors == null)
return EMPTY_TYPE_DESCRIPTOR_ARRAY;
else
return CollectionUtil.toArray(typeDescriptors.values(), TypeDescriptor.class);
}
@Override
public TypeDescriptor getTypeDescriptor(String tableName) {
logger.debug("getTypeDescriptor({})", tableName);
parseMetadataIfNecessary();
return typeDescriptors.get(tableName);
}
// StorageSystem interface -----------------------------------------------------------------------------------------
@Override
public void store(Entity entity) {
if (readOnly)
throw new IllegalStateException("Tried to insert rows into table '" + entity.type() + "' " +
"though database '" + id + "' is read-only");
logger.debug("Storing {}", entity);
persistOrUpdate(entity, true);
}
@Override
public void update(Entity entity) {
if (readOnly)
throw new IllegalStateException("Tried to update table '" + entity.type() + "' " +
"though database '" + id + "' is read-only");
logger.debug("Updating {}", entity);
persistOrUpdate(entity, false);
}
@Override
public void close() {
if (database != null)
CachingDBImporter.updateCacheFile(database);
IOUtil.close(importer);
}
public Entity queryEntityById(String tableName, Object id) {
try {
logger.debug("queryEntityById({}, {})", tableName, id);
ComplexTypeDescriptor descriptor = (ComplexTypeDescriptor) getTypeDescriptor(tableName);
PreparedStatement query = getSelectByPKStatement(descriptor);
query.setObject(1, id); // TODO v0.7.6 support composite keys
ResultSet resultSet = query.executeQuery();
if (resultSet.next())
return ResultSet2EntityConverter.convert(resultSet, descriptor);
else
return null;
} catch (SQLException e) {
throw new RuntimeException("Error querying " + tableName, e);
}
}
@Override
@SuppressWarnings("null")
public DataSource queryEntities(String type, String selector, Context context) {
logger.debug("queryEntities({})", type);
Connection connection = getConnection();
boolean script = false;
if (selector != null && selector.startsWith("{") && selector.endsWith("}")) {
selector = selector.substring(1, selector.length() - 1);
script = true;
}
String sql = null;
if (StringUtil.isEmpty(selector))
sql = "select * from " + type;
else if (StringUtil.startsWithIgnoreCase(selector, "select") || StringUtil.startsWithIgnoreCase(selector, "'select"))
sql = selector;
else if (selector.startsWith("ftl:") || !script)
sql = "select * from " + type + " WHERE " + selector;
else
sql = "'select * from " + type + " WHERE ' + " + selector;
if (script)
sql = '{' + sql + '}';
DataSource source = createQuery(sql, context, connection);
return new EntityResultSetDataSource(source, (ComplexTypeDescriptor) getTypeDescriptor(type));
}
public long countEntities(String tableName) {
logger.debug("countEntities({})", tableName);
String query = "select count(*) from " + tableName;
return DBUtil.queryLong(query, getConnection());
}
@Override
public DataSource> queryEntityIds(String tableName, String selector, Context context) {
logger.debug("queryEntityIds({}, {})", tableName, selector);
// check for script
boolean script = false;
if (selector != null && selector.startsWith("{") && selector.endsWith("}")) {
selector = selector.substring(1, selector.length() - 1);
script = true;
}
// find out pk columns
DBTable table = getTable(tableName);
String[] pkColumnNames = table.getPKColumnNames();
if (pkColumnNames.length == 0)
throw new ConfigurationError("Cannot create reference to table " + tableName + " since it does not define a primary key");
// construct selector
String query = "select " + ArrayFormat.format(pkColumnNames) + " from " + tableName;
if (selector != null) {
if (script)
query = "{'" + query + " where ' + " + selector + "}";
else
query += " where " + selector;
}
return query(query, true, context);
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public DataSource> query(String query, boolean simplify, Context context) {
logger.debug("query({})", query);
Connection connection = getConnection();
QueryDataSource resultSetIterable = createQuery(query, context, connection);
ResultSetConverter converter = new ResultSetConverter(Object.class, simplify);
return new ConvertingDataSource(resultSetIterable, converter);
}
public Consumer inserter() {
return new StorageSystemInserter(this);
}
public Consumer inserter(String tableName) {
return new StorageSystemInserter(this, (ComplexTypeDescriptor) getTypeDescriptor(tableName));
}
// database-specific interface -------------------------------------------------------------------------------------
public abstract Connection getConnection();
protected abstract PreparedStatement getSelectByPKStatement(ComplexTypeDescriptor descriptor);
public boolean tableExists(String tableName) {
logger.debug("tableExists({})", tableName);
return (getTypeDescriptor(tableName) != null);
}
public void createSequence(String name) throws SQLException {
getDialect().createSequence(name, 1, getConnection());
}
public void dropSequence(String name) {
execute(getDialect().renderDropSequence(name));
}
@Override
public Object execute(String sql) {
try {
DBUtil.executeUpdate(sql, getConnection());
return null;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public long nextSequenceValue(String sequenceName) {
return DBUtil.queryLong(getDialect().renderFetchSequenceValue(sequenceName), getConnection());
}
public void setSequenceValue(String sequenceName, long value) throws SQLException {
getDialect().setNextSequenceValue(sequenceName, value, getConnection());
}
protected Connection createConnection() {
try {
Connection connection = DBUtil.connect(url, driver, user, password, readOnly);
if (!connectedBefore) {
DBUtil.logMetaData(connection);
connectedBefore = true;
}
connection.setAutoCommit(false);
return connection;
} catch (ConnectFailedException e) {
throw new RuntimeException("Connecting the database failed. URL: " + url, e);
} catch (SQLException e) {
throw new ConfigurationError("Turning off auto-commit failed", e);
}
}
public void invalidate() {
typeDescriptors = null;
tables = null;
invalidationCount.incrementAndGet();
if (environment != null) {
File bufferFile = CachingDBImporter.getCacheFile(environment);
if (bufferFile.exists()) {
if (!bufferFile.delete() && metaDataCache) {
logger.error("Deleting " + bufferFile + " failed");
metaDataCache = false;
} else
logger.info("Deleted meta data cache file: " + bufferFile);
}
}
}
public int invalidationCount() {
return invalidationCount.get();
}
public void parseMetaData() {
this.tables = new HashMap();
this.typeDescriptors = OrderedNameMap.createCaseIgnorantMap();
//this.tableColumnIndexes = new HashMap>();
getDialect(); // make sure dialect is initialized
database = getDbMetaData();
if (lazy)
logger.info("Fetching table details and ordering tables by dependency");
else
logger.info("Ordering tables by dependency");
List tables = DBUtil.dependencyOrderedTables(database);
for (DBTable table : tables)
parseTable(table);
}
public DatabaseDialect getDialect() {
if (dialect == null) {
try {
DatabaseMetaData metaData = getConnection().getMetaData();
String productName = metaData.getDatabaseProductName();
VersionNumber productVersion = VersionNumber.valueOf(metaData.getDatabaseMajorVersion() + "." +
metaData.getDatabaseMinorVersion());
dialect = DatabaseDialectManager.getDialectForProduct(productName, productVersion);
} catch (SQLException e) {
throw new ConfigurationError("Database meta data access failed", e);
}
}
return dialect;
}
public String getSystem() {
return getDialect().getSystem();
}
public Database getDbMetaData() {
if (database == null)
fetchDbMetaData();
return database;
}
// java.lang.Object overrides ------------------------------------------------------------------
@Override
public String toString() {
return getClass().getSimpleName() + '[' + user + '@' + url + ']';
}
// private helpers ------------------------------------------------------------------------------
private void checkOracleDriverVersion(String driver) {
if (driver != null && driver.contains("oracle")) {
Connection connection = null;
try {
connection = getConnection();
DatabaseMetaData metaData = connection.getMetaData();
VersionNumber driverVersion = VersionNumber.valueOf(metaData.getDriverVersion());
if (driverVersion.compareTo(MIN_ORACLE_VERSION) < 0)
logger.warn("Your Oracle driver has a bug in metadata support. Please update to 10.2.0.4 or newer. " +
"You can use that driver for accessing an Oracle 9 server as well.");
} catch (SQLException e) {
throw new ConfigurationError(e);
} finally {
close();
}
}
}
private void fetchDbMetaData() {
try {
importer = createJDBCImporter();
if (metaDataCache)
importer = new CachingDBImporter((JDBCDBImporter) importer, getEnvironment());
database = importer.importDatabase();
} catch (ConnectFailedException e) {
throw new ConfigurationError("Database not available. ", e);
} catch (ImportFailedException e) {
throw new ConfigurationError("Unexpected failure of database meta data import. ", e);
}
}
private JDBCDBImporter createJDBCImporter() {
JDBCDBImporter importer = JDBCMetaDataUtil.getJDBCDBImporter(getConnection(), user, schemaName,
true, false, false, false, includeTables, excludeTables);
return importer;
}
private QueryDataSource createQuery(String query, Context context, Connection connection) {
return new QueryDataSource(connection, query, fetchSize, (dynamicQuerySupported ? context : null));
}
protected abstract PreparedStatement getStatement(ComplexTypeDescriptor descriptor, boolean insert,
List columnInfos);
private void parseTable(DBTable table) {
logger.debug("Parsing table {}" + table);
String tableName = table.getName();
tables.put(tableName.toUpperCase(), table);
ComplexTypeDescriptor complexType;
if (lazy)
complexType = new LazyTableComplexTypeDescriptor(table, this);
else
complexType = mapTableToComplexTypeDescriptor(table, new ComplexTypeDescriptor(tableName, this));
typeDescriptors.put(tableName, complexType);
}
public ComplexTypeDescriptor mapTableToComplexTypeDescriptor(DBTable table, ComplexTypeDescriptor complexType) {
// process primary keys
DBPrimaryKeyConstraint pkConstraint = table.getPrimaryKeyConstraint();
if (pkConstraint != null) {
String[] pkColumnNames = pkConstraint.getColumnNames();
if (pkColumnNames.length == 1) { // TODO v0.7.6 support composite primary keys
String columnName = pkColumnNames[0];
DBColumn column = table.getColumn(columnName);
table.getColumn(columnName);
String abstractType = JdbcMetaTypeMapper.abstractType(column.getType(), acceptUnknownColumnTypes);
IdDescriptor idDescriptor = new IdDescriptor(columnName, this, abstractType);
complexType.setComponent(idDescriptor);
}
}
// process foreign keys
for (DBForeignKeyConstraint constraint : table.getForeignKeyConstraints()) {
String[] foreignKeyColumnNames = constraint.getForeignKeyColumnNames();
if (foreignKeyColumnNames.length == 1) {
String fkColumnName = foreignKeyColumnNames[0];
DBTable targetTable = constraint.getRefereeTable();
DBColumn fkColumn = constraint.getTable().getColumn(fkColumnName);
DBDataType concreteType = fkColumn.getType();
String abstractType = JdbcMetaTypeMapper.abstractType(concreteType, acceptUnknownColumnTypes);
ReferenceDescriptor descriptor = new ReferenceDescriptor(
fkColumnName,
this,
abstractType,
targetTable.getName(),
constraint.getRefereeColumnNames()[0]);
descriptor.getLocalType(false).setSource(id);
descriptor.setMinCount(new ConstantExpression(1L));
descriptor.setMaxCount(new ConstantExpression(1L));
boolean nullable = fkColumn.isNullable();
descriptor.setNullable(nullable);
complexType.setComponent(descriptor); // overwrite possible id descriptor for foreign keys
logger.debug("Parsed reference " + table.getName() + '.' + descriptor);
} else {
// TODO v0.7.6 handle composite keys
}
}
// process normal columns
for (DBColumn column : table.getColumns()) {
try {
logger.debug("parsing column: {}", column);
String columnName = column.getName();
if (complexType.getComponent(columnName) != null)
continue; // skip columns that were already parsed (fk)
String columnId = table.getName() + '.' + columnName;
if (column.isVersionColumn()) {
logger.debug("Leaving out version column {}", columnId);
continue;
}
DBDataType columnType = column.getType();
String type = JdbcMetaTypeMapper.abstractType(columnType, acceptUnknownColumnTypes);
String defaultValue = column.getDefaultValue();
SimpleTypeDescriptor typeDescriptor = new SimpleTypeDescriptor(columnId, this, type);
if (defaultValue != null)
typeDescriptor.setDetailValue("constant", defaultValue);
if (column.getSize() != null)
typeDescriptor.setMaxLength(column.getSize());
if (column.getFractionDigits() != null) {
if ("timestamp".equals(type))
typeDescriptor.setGranularity("1970-01-02");
else
typeDescriptor.setGranularity(decimalGranularity(column.getFractionDigits()));
}
//typeDescriptors.put(typeDescriptor.getName(), typeDescriptor);
PartDescriptor descriptor = new PartDescriptor(columnName, this);
descriptor.setLocalType(typeDescriptor);
descriptor.setMinCount(new ConstantExpression(1L));
descriptor.setMaxCount(new ConstantExpression(1L));
descriptor.setNullable(column.getNotNullConstraint() == null);
List ukConstraints = column.getUkConstraints();
for (DBUniqueConstraint constraint : ukConstraints) {
if (constraint.getColumnNames().length == 1) {
descriptor.setUnique(true);
} else {
logger.warn("Automated uniqueness assurance on multiple columns is not provided yet: " + constraint);
// TODO v0.7.6 support uniqueness constraints on combination of columns
}
}
logger.debug("parsed attribute " + columnId + ": " + descriptor);
complexType.addComponent(descriptor);
} catch (Exception e) {
throw new ConfigurationError("Error processing column " + column.getName() + " of table " + table.getName(), e);
}
}
return complexType;
}
List getWriteColumnInfos(Entity entity, boolean insert) {
String tableName = entity.type();
DBTable table = getTable(tableName);
List pkColumnNames = CollectionUtil.toList(table.getPKColumnNames());
ComplexTypeDescriptor typeDescriptor = (ComplexTypeDescriptor) getTypeDescriptor(tableName);
Collection componentDescriptors = typeDescriptor.getComponents();
List pkInfos = new ArrayList(componentDescriptors.size());
List normalInfos = new ArrayList(componentDescriptors.size());
ComplexTypeDescriptor entityDescriptor = entity.descriptor();
for (ComponentDescriptor dbCompDescriptor : componentDescriptors) {
ComponentDescriptor enCompDescriptor = entityDescriptor.getComponent(dbCompDescriptor.getName());
if (enCompDescriptor != null && enCompDescriptor.getMode() == Mode.ignored)
continue;
if (dbCompDescriptor.getMode() != Mode.ignored) {
String name = dbCompDescriptor.getName();
SimpleTypeDescriptor type = (SimpleTypeDescriptor) dbCompDescriptor.getTypeDescriptor();
PrimitiveType primitiveType = type.getPrimitiveType();
if (primitiveType == null) {
if (!acceptUnknownColumnTypes)
throw new ConfigurationError("Column type of " + entityDescriptor.getName() + "." +
dbCompDescriptor.getName() + " unknown: " + type.getName());
else if (entity.get(type.getName()) instanceof String)
primitiveType = PrimitiveType.STRING;
else
primitiveType = PrimitiveType.OBJECT;
}
String primitiveTypeName = primitiveType.getName();
DBColumn column = table.getColumn(name);
DBDataType columnType = column.getType();
int sqlType = columnType.getJdbcType();
Class> javaType = driverTypeMapper.concreteType(primitiveTypeName);
ColumnInfo info = new ColumnInfo(name, sqlType, javaType);
if (pkColumnNames.contains(name))
pkInfos.add(info);
else
normalInfos.add(info);
}
}
if (insert) {
pkInfos.addAll(normalInfos);
return pkInfos;
} else {
normalInfos.addAll(pkInfos);
return normalInfos;
}
}
public DBTable getTable(String tableName) {
parseMetadataIfNecessary();
DBTable table = findTableInConfiguredCatalogAndSchema(tableName);
if (table != null)
return table;
table = findAnyTableOfName(tableName);
if (table != null) {
logger.warn("Table '" + tableName + "' not found " +
"in the expected catalog '" + catalogName + "' and schema '" + schemaName + "'. " +
"I have taken it from catalog '" + table.getCatalog() + "' and schema '" + table.getSchema() + "' instead. " +
"You better make sure this is right and fix the configuration");
return table;
}
throw new ObjectNotFoundException("Table " + tableName);
}
private DBTable findAnyTableOfName(String tableName) {
for (DBCatalog catalog : database.getCatalogs()) {
for (DBSchema schema : catalog.getSchemas()) {
DBTable table = schema.getTable(tableName);
if (table != null)
return table;
}
}
return null;
}
private DBTable findTableInConfiguredCatalogAndSchema(String tableName) {
DBCatalog catalog = database.getCatalog(catalogName);
if (catalog == null)
throw new ConfigurationError("Catalog '" + catalogName + "' not found in database '" + id + "'");
DBSchema dbSchema = catalog.getSchema(schemaName);
if (dbSchema != null) {
DBTable table = dbSchema.getTable(tableName);
if (table != null)
return table;
}
return null;
}
private void persistOrUpdate(Entity entity, boolean insert) {
parseMetadataIfNecessary();
List writeColumnInfos = getWriteColumnInfos(entity, insert);
try {
String tableName = entity.type();
PreparedStatement statement = getStatement(entity.descriptor(), insert, writeColumnInfos);
for (int i = 0; i < writeColumnInfos.size(); i++) {
ColumnInfo info = writeColumnInfos.get(i);
Object componentValue = entity.getComponent(info.name);
Object jdbcValue = componentValue;
if (info.type != null)
jdbcValue = AnyConverter.convert(jdbcValue, info.type);
try {
boolean criticalOracleType = (dialect instanceof OracleDialect && (info.sqlType == Types.NCLOB || info.sqlType == Types.OTHER));
if (jdbcValue != null || criticalOracleType) // Oracle is not able to perform setNull() on NCLOBs and NVARCHAR2
statement.setObject(i + 1, jdbcValue);
else
statement.setNull(i + 1, info.sqlType);
} catch (SQLException e) {
throw new RuntimeException("error setting column " + tableName + '.' + info.name, e);
}
}
if (batch) {
statement.addBatch();
} else {
int rowCount = statement.executeUpdate();
if (rowCount == 0)
throw new RuntimeException("Update failed because, since there is no database entry with the PK of " + entity);
}
} catch (Exception e) {
throw new RuntimeException("Error in persisting " + entity, e);
}
}
private void parseMetadataIfNecessary() {
if (typeDescriptors == null)
parseMetaData();
}
private static String decimalGranularity(int scale) {
if (scale == 0)
return "1";
StringBuilder builder = new StringBuilder("0.");
for (int i = 1; i < scale; i++)
builder.append('0');
builder.append(1);
return builder.toString();
}
private static TypeMapper driverTypeMapper() {
return new TypeMapper(
"byte", Byte.class,
"short", Short.class,
"int", Integer.class,
"big_integer", Long.class,
"float", Float.class,
"double", Double.class,
"big_decimal", BigDecimal.class,
"boolean", Boolean.class,
"char", Character.class,
"date", java.sql.Date.class,
"timestamp", java.sql.Timestamp.class,
"string", java.sql.Clob.class,
"string", String.class,
"binary", Blob.class,
"binary", byte[].class
// "object", Object.class,
);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy