/* ==================================================================
* JdbcUtils.java - 6/10/2016 12:54:09 PM
*
* Copyright 2007-2016 SolarNetwork.net Dev Team
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ==================================================================
*/
package net.solarnetwork.node.dao.jdbc;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.LinkedHashMap;
import java.util.Map;
import org.supercsv.cellprocessor.ConvertNullTo;
import org.supercsv.cellprocessor.Optional;
import org.supercsv.cellprocessor.ParseBigDecimal;
import org.supercsv.cellprocessor.ParseBool;
import org.supercsv.cellprocessor.ift.CellProcessor;
/**
* Utilities to help with JDBC.
*
* @author matt
* @version 1.0
* @since 1.17
*/
public abstract class JdbcUtils {
private JdbcUtils() {
// don't construct me
}
/**
* Get a set of {@link CellProcessor} for formatting ResultSet data as CSV
* strings.
*
* @param meta
* The metadata.
* @return The processors.
* @throws SQLException
* If any SQL error occurs.
*/
public static CellProcessor[] formattingProcessorsForResultSetMetaData(ResultSetMetaData meta)
throws SQLException {
int colCount = meta.getColumnCount();
CellProcessor[] cellProcessors = new CellProcessor[colCount];
for ( int i = 0; i < colCount; i++ ) {
CellProcessor processor = null;
int sqlType = meta.getColumnType(i + 1);
switch (sqlType) {
case Types.DATE:
processor = new ConvertNullTo("", new JdbcFmtDate.Date());
break;
case Types.TIME:
processor = new ConvertNullTo("", new JdbcFmtDate.Time());
break;
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
processor = new ConvertNullTo("", new JdbcFmtDate.Timestamp());
break;
}
cellProcessors[i] = processor;
}
return cellProcessors;
}
/**
* Get a set of {@link CellProcessor} for parsing CSV strings into JDBC
* column objects.
*
* @param csvColumns
* The parsed CSV column names (i.e. from the header row).
* @param columnMetaData
* JDBC column metadata (i.e. extracted from JDBC via
* {@link #columnCsvMetaDataForDatabaseMetaData(DatabaseMetaData, String)})
* @return The cell processors.
*/
public static CellProcessor[] parsingCellProcessorsForCsvColumns(String[] csvColumns,
Map<String, ColumnCsvMetaData> columnMetaData) {
CellProcessor[] result = new CellProcessor[csvColumns.length];
int i = 0;
for ( String colName : csvColumns ) {
ColumnCsvMetaData meta = columnMetaData.get(colName);
result[i++] = (meta != null && meta.getCellProcessor() != null ? meta.getCellProcessor()
: new Optional());
}
return result;
}
/**
* Get a mapping of JDBC column names to associated column metadata from a
* JDBC {@link DatabaseMetaData} object.
*
* @param meta
* The database metadata to read from.
* @param tableName
* The table name to get column metadata for.
* @return The metadata.
* @throws SQLException
* If any SQL error occurs.
*/
public static Map<String, ColumnCsvMetaData> columnCsvMetaDataForDatabaseMetaData(
DatabaseMetaData meta, String tableName) throws SQLException {
String[] names = tableName.toUpperCase().split("\\.", 2);
String schema = (names.length == 2 ? names[0] : null);
String table = (names.length == 2 ? names[1] : names[0]);
ResultSet rs = meta.getColumns(null, schema, table, null);
Map<String, ColumnCsvMetaData> results = new LinkedHashMap<String, ColumnCsvMetaData>(8);
try {
while ( rs.next() ) {
String colName = rs.getString(4);
int sqlType = rs.getInt(5);
CellProcessor processor = null;
switch (sqlType) {
case Types.BOOLEAN:
processor = new ParseBool();
break;
case Types.DATE:
processor = new JdbcParseDate.Date();
break;
case Types.TIME:
processor = new JdbcParseDate.Time();
break;
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
processor = new JdbcParseDate.Timestamp();
break;
case Types.BIGINT:
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.INTEGER:
case Types.NUMERIC:
case Types.REAL:
case Types.SMALLINT:
case Types.TINYINT:
processor = new ParseBigDecimal();
break;
}
results.put(colName, new ColumnCsvMetaData(colName,
(processor == null ? new Optional() : new Optional(processor)), sqlType));
}
} finally {
rs.close();
}
// get primary key status
rs = meta.getPrimaryKeys(null, schema, table);
try {
while ( rs.next() ) {
String colName = rs.getString(4);
ColumnCsvMetaData colMeta = results.get(colName);
if ( colMeta != null ) {
results.put(colName, colMeta.asPrimaryKeyColumn());
}
}
} finally {
rs.close();
}
return results;
}
/**
* Get a SQL string for inserting into a table using column metadata.
*
* @param tableName
* The table name to insert into.
* @param columnMetaData
* The column metadata of that table, i.e. from
* {@link #columnCsvMetaDataForDatabaseMetaData(DatabaseMetaData, String)}.
* @return The SQL statement.
*/
public static String insertSqlForColumnCsvMetaData(String tableName,
Map<String, ColumnCsvMetaData> columnMetaData) {
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append(" (");
StringBuilder values = new StringBuilder();
int i = 0;
for ( Map.Entry<String, ColumnCsvMetaData> me : columnMetaData.entrySet() ) {
if ( i > 0 ) {
sql.append(",");
values.append(",");
}
sql.append(me.getKey());
values.append("?");
i++;
}
sql.append(") VALUES (").append(values).append(")");
return sql.toString();
}
/**
* Get a mapping of CVS column names to their associated position in an
* array.
*
* @param header
* The parsed CVS column header row.
* @return The mapping of headers. The iteration order preserves the order
* of the array.
*/
public static Map<String, Integer> csvColumnIndexMapping(String[] header) {
Map<String, Integer> csvColumns = new LinkedHashMap<String, Integer>();
for ( int i = 0; i < header.length; i++ ) {
csvColumns.put(header[i], i);
}
return csvColumns;
}
}