All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dk.eobjects.metamodel.JdbcDataContextStrategy Maven / Gradle / Ivy

The newest version!
/**
 *  This file is part of MetaModel.
 *
 *  MetaModel is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MetaModel is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MetaModel.  If not, see .
 */
package dk.eobjects.metamodel;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import javax.sql.DataSource;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import dk.eobjects.metamodel.data.DataSet;
import dk.eobjects.metamodel.data.MaxRowsDataSetStrategyWrapper;
import dk.eobjects.metamodel.dialects.DefaultQueryRewriter;
import dk.eobjects.metamodel.dialects.IQueryRewriter;
import dk.eobjects.metamodel.dialects.MysqlQueryRewriter;
import dk.eobjects.metamodel.dialects.PostgresqlQueryRewriter;
import dk.eobjects.metamodel.dialects.SQLServerQueryRewriter;
import dk.eobjects.metamodel.query.Query;
import dk.eobjects.metamodel.query.SelectItem;
import dk.eobjects.metamodel.schema.Column;
import dk.eobjects.metamodel.schema.ColumnType;
import dk.eobjects.metamodel.schema.JdbcColumn;
import dk.eobjects.metamodel.schema.JdbcSchema;
import dk.eobjects.metamodel.schema.JdbcTable;
import dk.eobjects.metamodel.schema.Relationship;
import dk.eobjects.metamodel.schema.Schema;
import dk.eobjects.metamodel.schema.Table;
import dk.eobjects.metamodel.schema.TableType;

/**
 * DataContextStrategy to use for JDBC-compliant databases
 */
public class JdbcDataContextStrategy implements IDataContextStrategy {

	public static final String DATABASE_PRODUCT_POSTGRESQL = "PostgreSQL";
	public static final String DATABASE_PRODUCT_MYSQL = "MySQL";
	public static final String DATABASE_PRODUCT_HSQLDB = "HSQL Database Engine";
	public static final String DATABASE_PRODUCT_SQLSERVER = "Microsoft SQL Server";
	public static final int FETCH_SIZE = 25000;
	private static final Log _log = LogFactory
			.getLog(JdbcDataContextStrategy.class);

	private Connection _connection;
	private TableType[] _tableTypes;
	private String _catalogName;
	private DataSource _dataSource;
	private String _databaseProductName;
	private IQueryRewriter _queryRewriter = new DefaultQueryRewriter();

	/**
	 * There are some confusion as to the definition of catalogs and schemas.
	 * Some databases seperate "groups of tables" by using schemas, others by
	 * catalogs. This variable indicates whether a MetaModel schema really
	 * represents a catalog.
	 */
	private boolean _usesCatalogsAsSchemas;
	private String _identifierQuoteString;

	/**
	 * Creates the strategy based on a data source, some table types and an
	 * optional catalogName
	 * 
	 * @param dataSource
	 *            the datasource objcet to use for making connections
	 * @param tableTypes
	 *            the types of tables to include
	 * @param catalogName
	 *            a catalog name to use, can be null
	 */
	public JdbcDataContextStrategy(DataSource dataSource,
			TableType[] tableTypes, String catalogName) {
		_dataSource = dataSource;
		_tableTypes = tableTypes;
		_catalogName = catalogName;
		initialize();
	}

	/**
	 * Creates the strategy based on a connection, some table types and an
	 * optional catalogName
	 * 
	 * @param connection
	 *            the database connection
	 * @param tableTypes
	 *            the types of tables to include
	 * @param catalogName
	 *            a catalog name to use, can be null
	 */
	public JdbcDataContextStrategy(Connection connection,
			TableType[] tableTypes, String catalogName) {
		_connection = connection;
		_tableTypes = tableTypes;
		_catalogName = catalogName;
		initialize();
	}

	private void initialize() {
		try {
			DatabaseMetaData metaData = getConnection().getMetaData();

			try {
				_identifierQuoteString = metaData.getIdentifierQuoteString();
			} catch (SQLException e) {
				_log.warn(e);
			}

			_usesCatalogsAsSchemas = usesCatalogsAsSchemas(metaData);
			try {
				_databaseProductName = metaData.getDatabaseProductName();
				if (_log.isDebugEnabled()) {
					_log
							.debug("Database product name: "
									+ _databaseProductName);
				}
				if (DATABASE_PRODUCT_MYSQL.equals(_databaseProductName)) {
					setQueryRewriter(new MysqlQueryRewriter());
				} else if (DATABASE_PRODUCT_POSTGRESQL
						.equals(_databaseProductName)) {
					setQueryRewriter(new PostgresqlQueryRewriter());
				} else if (DATABASE_PRODUCT_SQLSERVER
						.equals(_databaseProductName)) {
					setQueryRewriter(new SQLServerQueryRewriter());
				}

			} catch (SQLException e) {
				_log.warn("Could not retrieve database product name: "
						+ e.getMessage());
			}
		} catch (SQLException e) {
			_log.debug("initialize(): " + e.getMessage());
		}
	}

	private boolean usesCatalogsAsSchemas(DatabaseMetaData metaData) {
		boolean result = true;
		ResultSet rs = null;
		try {
			rs = metaData.getSchemas();
			while (rs.next() && result) {
				result = false;
			}
		} catch (SQLException e) {
			_log.warn("Could not retrieve schema and catalog information: "
					+ e.getMessage());
			result = false;
		} finally {
			close(rs, null);
		}
		return result;
	}

	private void loadTables(JdbcSchema schema, DatabaseMetaData metaData,
			String[] types) {
		ResultSet rs = null;
		try {
			if (_log.isInfoEnabled()) {
				_log.info("Querying for table types "
						+ ArrayUtils.toString(types) + " in catalog: "
						+ _catalogName + ", schema: " + schema.getName());
			}
			if (_usesCatalogsAsSchemas) {
				rs = metaData.getTables(schema.getName(), null, null, types);
			} else {
				rs = metaData.getTables(_catalogName, schema.getName(), null,
						types);
			}
			while (rs.next()) {
				String tableCatalog = rs.getString(1);
				String tableSchema = rs.getString(2);
				String tableName = rs.getString(3);
				String tableTypeName = rs.getString(4);
				TableType tableType = TableType.getTableType(tableTypeName);
				String tableRemarks = rs.getString(5);

				if (_log.isDebugEnabled()) {
					_log.debug("Found table: tableCatalog=" + tableCatalog
							+ ",tableSchema=" + tableSchema + ",tableName="
							+ tableName);
				}

				if (tableSchema == null) {
					tableSchema = tableCatalog;
				}

				JdbcTable table = new JdbcTable(tableName, tableType, schema,
						this);
				table.setRemarks(tableRemarks);
				schema.addTable(table);
			}
		} catch (SQLException e) {
			_log.error("Error retriving table metadata", e);
		} finally {
			close(rs, null);
		}
	}

	/**
	 * Loads index metadata for a table
	 * 
	 * @param table
	 */
	public void loadIndexes(JdbcTable table) {
		try {
			DatabaseMetaData metaData = getConnection().getMetaData();
			loadIndexes(table, metaData);
		} catch (Exception e) {
			_log.error(e);
		}
	}

	private void loadIndexes(Table table, DatabaseMetaData metaData) {
		Schema schema = table.getSchema();
		ResultSet rs = null;
		// Ticket #170: IndexInfo is nice-to-have, not need-to-have, so
		// we will do a nice failover on SQLExceptions
		try {
			if (_usesCatalogsAsSchemas) {
				rs = metaData.getIndexInfo(schema.getName(), null, table
						.getName(), false, true);
			} else {
				rs = metaData.getIndexInfo(_catalogName, schema.getName(),
						table.getName(), false, true);
			}
			while (rs.next()) {
				String columnName = rs.getString(9);
				if (columnName != null) {
					Column column = table.getColumnByName(columnName);
					if (column != null) {
						column.setIndexed(true);
					} else {
						_log.error("Indexed column \"" + columnName
								+ "\" could not be found in table: " + table);
					}
				}
			}
		} catch (SQLException e) {
			_log.warn("Could not retrieve index info for table: "
					+ table.getName());
			if (_log.isDebugEnabled()) {
				_log.debug(e);
			} else if (_log.isInfoEnabled()) {
				_log.info(e.getMessage());
			}
		} finally {
			close(rs, null);
		}
	}

	/**
	 * Loads column metadata (no indexes though) for a table
	 * 
	 * @param table
	 */
	public void loadColumns(JdbcTable table) {
		try {
			DatabaseMetaData metaData = getConnection().getMetaData();
			loadColumns(table, metaData);
		} catch (Exception e) {
			_log.error(e);
		}
	}

	private void loadColumns(JdbcTable table, DatabaseMetaData metaData) {
		Schema schema = table.getSchema();
		ResultSet rs = null;
		try {
			if (_log.isInfoEnabled()) {
				_log.info("Querying for columns in table: " + table.getName());
			}
			table.setQuote(_identifierQuoteString);
			int columnNumber = -1;
			if (_usesCatalogsAsSchemas) {
				rs = metaData.getColumns(schema.getName(), null, table
						.getName(), null);
			} else {
				rs = metaData.getColumns(_catalogName, schema.getName(), table
						.getName(), null);
			}
			while (rs.next()) {
				columnNumber++;
				String columnName = rs.getString(4);
				if (_identifierQuoteString == null
						&& new StringTokenizer(columnName).countTokens() > 1) {
					_log.warn("column name contains whitespace: \""
							+ columnName + "\".");
				}

				int jdbcType = rs.getInt(5);
				ColumnType columnType = ColumnType.convertColumnType(jdbcType);

				String nativeType = rs.getString(6);
				Integer columnSize = rs.getInt(7);

				int jdbcNullable = rs.getInt(11);
				Boolean nullable = null;
				if (jdbcNullable == DatabaseMetaData.columnNullable) {
					nullable = true;
				} else if (jdbcNullable == DatabaseMetaData.columnNoNulls) {
					nullable = false;
				}

				String remarks = rs.getString(12);

				Column column = new JdbcColumn(columnName, columnType, table,
						columnNumber, nullable);
				column.setRemarks(remarks);
				column.setNativeType(nativeType);
				column.setColumnSize(columnSize);
				column.setQuote(_identifierQuoteString);
				table.addColumn(column);
			}
		} catch (SQLException e) {
			_log.error("Error retriving table metadata", e);
		} finally {
			close(rs, null);
		}
	}

	public void loadRelations(Schema schema) {
		Table[] tables = schema.getTables();
		try {
			DatabaseMetaData metaData = getConnection().getMetaData();
			for (Table table : tables) {
				loadRelations(table, metaData);
			}
		} catch (Exception e) {
			_log.error(e);
		}
	}

	private void loadRelations(Table table, DatabaseMetaData metaData) {
		Schema schema = table.getSchema();
		ResultSet rs = null;
		try {
			if (_usesCatalogsAsSchemas) {
				rs = metaData.getImportedKeys(schema.getName(), null, table
						.getName());
			} else {
				rs = metaData.getImportedKeys(_catalogName, schema.getName(),
						table.getName());
			}
			loadRelations(rs, schema);
		} catch (SQLException e) {
			_log.error("Error retriving table metadata", e);
		} finally {
			close(rs, null);
		}
	}

	private void loadRelations(ResultSet rs, Schema schema) throws SQLException {
		while (rs.next()) {
			String pkTableName = rs.getString(3);
			String pkColumnName = rs.getString(4);

			Column pkColumn = null;
			Table pkTable = schema.getTableByName(pkTableName);
			if (pkTable != null) {
				pkColumn = pkTable.getColumnByName(pkColumnName);
			}
			if (_log.isDebugEnabled()) {
				_log.debug("Found primary key relation: tableName="
						+ pkTableName + ",columnName=" + pkColumnName
						+ ", matching column: " + pkColumn);
			}

			String fkTableName = rs.getString(7);
			String fkColumnName = rs.getString(8);
			Column fkColumn = null;
			Table fkTable = schema.getTableByName(fkTableName);
			if (fkTable != null) {
				fkColumn = fkTable.getColumnByName(fkColumnName);
			}
			if (_log.isDebugEnabled()) {
				_log.debug("Found foreign key relation: tableName="
						+ fkTableName + ",columnName=" + fkColumnName
						+ ", matching column: " + fkColumn);
			}

			if (pkColumn == null || fkColumn == null) {
				_log.error("Could not find relation columns: pkTableName="
						+ pkTableName + ",pkColumnName=" + pkColumnName
						+ "fkTableName=" + fkTableName + ",fkColumnName="
						+ fkColumnName);
				_log.error("pkColumn=" + pkColumn);
				_log.error("fkColumn=" + fkColumn);
			} else {
				Relationship[] relations = pkTable.getRelationships(fkTable);
				boolean exists = false;
				for (Relationship relation : relations) {
					if (relation.containsColumnPair(pkColumn, fkColumn)) {
						exists = true;
						break;
					}
				}
				if (!exists) {
					Relationship.createRelationship(new Column[] { pkColumn },
							new Column[] { fkColumn });
				}
			}
		}
	}

	public DataSet executeQuery(Query query) throws MetaModelException {
		Statement statement = null;
		ResultSet resultSet = null;
		try {
			String queryString;
			if (_queryRewriter != null) {
				queryString = _queryRewriter.rewriteQuery(this, query);
			} else {
				queryString = query.toString();
			}
			Connection connection = getConnection();
			statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,
					ResultSet.CONCUR_READ_ONLY);

			try {
				statement.setFetchSize(FETCH_SIZE);
			} catch (Exception e) {
				// Ticket #372: Sometimes an exception is thrown here even
				// though it's contrary to the jdbc spec. We'll proceed without
				// doing anything about it though.
				_log.warn("Could not setFetchSize on statement: "
						+ e.getMessage());
			}

			boolean postProcessMaxRows = false;
			Integer maxRows = query.getMaxRows();
			if (maxRows != null) {
				try {
					statement.setMaxRows(maxRows);
				} catch (SQLException e) {
					_log.debug("setMaxRows(" + maxRows + ") was rejected.", e);
					postProcessMaxRows = true;
				}
			}

			_log.info(queryString);
			resultSet = statement.executeQuery(queryString);
			DataSet dataSet = new DataSet(query, statement, resultSet);

			if (postProcessMaxRows) {
				dataSet = new DataSet(new MaxRowsDataSetStrategyWrapper(
						dataSet, maxRows));
			}
			return dataSet;
		} catch (SQLException e) {
			_log.warn("Exception occurred when executing query: "
					+ e.getMessage());
			close(resultSet, statement);
			if (isComplexQuery(query)) {
				_log
						.info("Trying to query again with failover strategy, this may be a performance problem.");
				IDataContextStrategy failoverStrategy = getFailoverStrategy(getConnection());
				return failoverStrategy.executeQuery(query);
			} else {
				throw new MetaModelException("Could not execute query", e);
			}
		}
	}

	/**
	 * Determines if a query is of a complex type which may be error-prone to
	 * execute on some databases and therefore should be tried to execute with a
	 * query postprocessing strategy
	 */
	private boolean isComplexQuery(Query q) {
		if (q.getWhereClause().getItemCount() > 0) {
			return true;
		}
		if (q.getGroupByClause().getItemCount() > 0) {
			return true;
		}
		if (q.getHavingClause().getItemCount() > 0) {
			return true;
		}
		if (q.getOrderByClause().getItemCount() > 0) {
			return true;
		}
		return false;
	}

	/**
	 * Creates a IDataContextStrategy that uses a more fault-tolerant
	 * query-postprocessing strategy.
	 */
	private IDataContextStrategy getFailoverStrategy(final Connection con) {
		IDataContextStrategy failoverStrategy = new QueryPostprocessDataContextStrategy() {

			@Override
			public DataSet materializeMainSchemaTable(Table table,
					Column[] columns, int maxRows) {
				Statement statement = null;
				ResultSet resultSet = null;
				try {
					SelectItem[] selectItems = new SelectItem[columns.length];
					statement = con.createStatement();
					StringBuilder sb = new StringBuilder("SELECT ");
					for (int i = 0; i < columns.length; i++) {
						Column column = columns[i];
						selectItems[i] = new SelectItem(column);
						if (i != 0) {
							sb.append(", ");
						}
						sb.append(columns[i].getQuotedName());
					}
					sb.append(" FROM ");
					if (table.getSchema() != null
							&& table.getSchema().getName() != null) {
						sb.append(table.getSchema().getName() + '.');
					}
					sb.append(table.getQuotedName());
					String queryString = sb.toString();
					_log.info("Using failover query: " + queryString);
					if (maxRows != -1) {
						statement.setMaxRows(maxRows);
					}
					resultSet = statement.executeQuery(queryString);
					DataSet dataSet = new DataSet(selectItems, statement,
							resultSet);
					return dataSet;
				} catch (SQLException e) {
					close(resultSet, statement);
					throw new MetaModelException(e);
				}
			}

			@Override
			protected Schema getMainSchema() throws MetaModelException {
				return JdbcDataContextStrategy.this
						.getSchemaByName(getMainSchemaName());
			}

			@Override
			protected String getMainSchemaName() throws MetaModelException {
				return JdbcDataContextStrategy.this.getDefaultSchemaName();
			}
		};
		return failoverStrategy;
	}

	/**
	 * Quietly closes any of the parameterized JDBC objects
	 * 
	 * @param rs
	 * @param st
	 */
	public static void close(ResultSet rs, Statement st) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				_log.info("Could not close ResultSet", e);
			}
		}
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				_log.info("Could not close Statement", e);
			}
		}
	}

	/**
	 * Convenience method to get the available catalogNames using this
	 * connection.
	 * 
	 * @return a String-array with the names of the available catalogs.
	 */
	public String[] getCatalogNames() {
		// Retrieve metadata
		DatabaseMetaData metaData = null;
		ResultSet rs = null;
		try {
			metaData = _connection.getMetaData();
		} catch (SQLException e) {
			_log.error("Error retrieving metadata", e);
			throw new MetaModelException(e);
		}

		// Retrieve catalogs
		_log.info("Retrieving catalogs");
		List catalogs = new ArrayList();
		try {
			rs = metaData.getCatalogs();
			while (rs.next()) {
				String catalogName = rs.getString(1);
				_log.debug("Found catalogName: " + catalogName);
				catalogs.add(catalogName);
			}
		} catch (SQLException e) {
			_log.error("Error retrieving catalog metadata", e);
		} finally {
			close(rs, null);
			_log.info("Retrieved " + catalogs.size() + " catalogs");
		}
		return catalogs.toArray(new String[catalogs.size()]);
	}

	/**
	 * Gets an appropriate connection object to use - either a dedicated
	 * connection or a new connection from the datasource object
	 */
	private Connection getConnection() {
		if (_dataSource == null) {
			return _connection;
		}
		try {
			return _dataSource.getConnection();
		} catch (SQLException e) {
			throw new MetaModelException("Could not establish connection", e);
		}
	}

	public String getDefaultSchemaName() {
		// Use a boolean to check if the result has been
		// found, because a schema name can actually be
		// null (for example in the case of Firebird
		// databases).
		boolean found = false;
		String result = null;
		try {
			String[] schemaNames = getSchemaNames();

			// First strategy: If there's only one schema available, that must
			// be it
			if (schemaNames.length == 1) {
				result = schemaNames[0];
				found = true;
			}

			if (!found) {
				Connection connection = getConnection();
				DatabaseMetaData metaData = connection.getMetaData();
				// Second strategy: Find default schema name by examining the
				// URL
				if (!found) {
					String url = metaData.getURL();
					if (url != null && url.length() > 0) {
						if (schemaNames.length > 0) {
							StringTokenizer st = new StringTokenizer(url,
									"/\\:");
							int tokenCount = st.countTokens();
							if (tokenCount > 0) {
								for (int i = 1; i < tokenCount; i++) {
									st.nextToken();
								}
								String lastToken = st.nextToken();

								for (int i = 0; i < schemaNames.length
										&& !found; i++) {
									String schemaName = schemaNames[i];
									if (lastToken.indexOf(schemaName) != -1) {
										result = schemaName;
										found = true;
									}
								}
							}
						}
					}
				}

				// Third strategy: Check for schema equal to username
				if (!found) {
					String username = metaData.getUserName();
					if (username != null) {
						for (int i = 0; i < schemaNames.length && !found; i++) {
							if (username.equalsIgnoreCase(schemaNames[i])) {
								result = schemaNames[i];
								found = true;
							}
						}
					}
				}

				// Fourth strategy: Find default schema name by vendor-specific
				// hacks
				if (!found) {
					if (DATABASE_PRODUCT_POSTGRESQL
							.equalsIgnoreCase(_databaseProductName)) {
						if (_catalogName == null) {
							result = "public";
						} else {
							result = _catalogName;
						}
						found = true;
					}
					if (DATABASE_PRODUCT_HSQLDB
							.equalsIgnoreCase(_databaseProductName)) {
						for (int i = 0; i < schemaNames.length && !found; i++) {
							String schemaName = schemaNames[i];
							if ("PUBLIC".equals(schemaName)) {
								result = schemaName;
								found = true;
							}
						}
					}
				}
			}
		} catch (SQLException e) {
			_log.error("Could not determine default schema name: "
					+ e.getMessage());
		}
		return result;
	}

	public Schema getSchemaByName(String name) throws MetaModelException {
		DatabaseMetaData metaData;
		try {
			metaData = getConnection().getMetaData();
		} catch (SQLException e) {
			throw new MetaModelException(e);
		}
		JdbcSchema schema = new JdbcSchema(name, this);

		// Creates string array to represent the table types
		String[] types = getTableTypeAsString();
		loadTables(schema, metaData, types);

		return schema;
	}

	public String[] getSchemaNames() throws MetaModelException {
		try {
			DatabaseMetaData metaData = getConnection().getMetaData();
			Collection result = new ArrayList();

			if (DATABASE_PRODUCT_SQLSERVER.equals(_databaseProductName)) {
				result = getSchemaSQLServerNames();
			} else if (_usesCatalogsAsSchemas) {
				String[] catalogNames = getCatalogNames();
				for (String name : catalogNames) {
					_log.info("Found catalogName: " + name);
					result.add(name);
				}
			} else {
				ResultSet rs = metaData.getSchemas();
				while (rs.next()) {
					String schemaName = rs.getString(1);
					_log.info("Found schemaName: " + schemaName);
					result.add(schemaName);
				}
				rs.close();
			}

			if (DATABASE_PRODUCT_MYSQL.equals(_databaseProductName)) {
				result.remove("information_schema");
			}

			// If still no schemas are found, add a schema with a null-name
			if (result.isEmpty()) {
				_log
						.info("No schemas or catalogs found. Creating unnamed schema.");
				result.add(null);
			}
			return result.toArray(new String[result.size()]);
		} catch (SQLException e) {
			throw new MetaModelException(e);
		}
	}

	/**
	 * Microsoft SQL Server returns users instead of schemas when calling
	 * metadata.getSchemas() This is a simple workaround.
	 * 
	 * @return
	 * @throws SQLException
	 */
	private Set getSchemaSQLServerNames() throws SQLException {
		// Distinct schema names. metaData.getTables() is a denormalized
		// resultset
		Set schemas = new HashSet();
		DatabaseMetaData metaData = getConnection().getMetaData();
		ResultSet rs = metaData.getTables(_catalogName, null, null,
				getTableTypeAsString());
		while (rs.next()) {
			schemas.add(rs.getString("TABLE_SCHEM"));
		}
		return schemas;

	}

	private String[] getTableTypeAsString() {
		String[] types = new String[_tableTypes.length];
		for (int i = 0; i < types.length; i++) {
			if (_tableTypes[i] == TableType.OTHER) {
				// if the OTHER type has been selected, don't use a table
				// pattern (ie. include all types)
				types = null;
				break;
			}
			types[i] = _tableTypes[i].toString();
		}
		return types;
	}

	public JdbcDataContextStrategy setQueryRewriter(IQueryRewriter queryRewriter) {
		_queryRewriter = queryRewriter;
		return this;
	}

	public IQueryRewriter getQueryRewriter() {
		return _queryRewriter;
	}

	public String getIdentifierQuoteString() {
		return _identifierQuoteString;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy