Java tutorial
/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.poulpe.util.databasebackup.persistence; import org.apache.commons.lang.Validate; import org.jtalks.poulpe.util.databasebackup.domain.ColumnMetaData; import org.jtalks.poulpe.util.databasebackup.domain.Row; import org.jtalks.poulpe.util.databasebackup.exceptions.RowProcessingException; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import javax.sql.DataSource; import java.sql.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * The class is responsible for providing table structure and table data. * * @author Evgeny Surovtsev * */ class DbTableData { /** * Constructs a new instance of the class with given DataSource and TableName. These parameters will be used for * preparing table's structure and data. * * @param dataSource * a DataSource object for accessing the table. * @param tableName * a name of the table to be dumped. */ public DbTableData(DataSource dataSource, String tableName) { Validate.notNull(dataSource, "dataSource parameter must not be null."); Validate.notEmpty(tableName, "tableName parameter must not be empty."); this.dataSource = dataSource; this.tableName = tableName; this.jdbcTemplate = new JdbcTemplate(dataSource); } /** * The method prepares table's data in the shape and passes every {@link Row} into given RowProcessor. * * @param processor * injected logic to perform some actions under passing rows. see details for {@link RowProcessor}. * @throws SQLException * if any errors during work with database occur. */ public void getData(final RowProcessor processor) throws SQLException { try { jdbcTemplate.query(SELECT_FROM + tableName, new RowCallbackHandler() { @Override public void processRow(ResultSet rs) throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); Row row = new Row(); for (int i = 1; i <= columnCount; i++) { ColumnMetaData columnMetaData = ColumnMetaData.getInstance(metaData.getColumnName(i), SqlTypes.getSqlTypeByJdbcSqlType(metaData.getColumnType(i))); row.addCell(columnMetaData, rs.getObject(i)); } try { processor.process(row); } catch (RowProcessingException e) { throw new SQLException(e); } } }); } catch (DataAccessException e) { throw new SQLException(e); } } /** * Returns the structure of the table in the shape of list of Table columns. * * @return A list of Table column elements. * @throws SQLException * Is thrown in case any errors during work with database occur. */ public List<ColumnMetaData> getStructure() throws SQLException { List<ColumnMetaData> tableColumnList = new ArrayList<ColumnMetaData>(); Statement stmt = null; ResultSet rs = null; ResultSetMetaData rsmd = null; Connection connection = null; try { // Get a list of defaults for the column // this cannot be done via ResultSetMetaData, so doing this via tableMetaData instead connection = dataSource.getConnection(); DatabaseMetaData dbMetaData = connection.getMetaData(); Map<String, String> columnDefaultValues = getColumnDefaults(dbMetaData); // Taking the rest of information from ResultSetMetaData object stmt = connection.createStatement(); // WHERE 1 = 0 -- we don't need actual data, just a table structure, so lets make the query's result empty. rs = stmt.executeQuery(SELECT_FROM + tableName + " WHERE 1 = 0"); rsmd = rs.getMetaData(); int numberOfColumns = rsmd.getColumnCount(); for (int i = 1; i <= numberOfColumns; i++) { tableColumnList.add(getColumnMetaData(rsmd, columnDefaultValues, i)); } } finally { if (stmt != null) { stmt.close(); } if (connection != null) { connection.close(); } } return tableColumnList; } /** * Constructs a new ColumnMetaData objects from given ResultSetMetaData with provided column default values map and * the object's index. * * @param rsmd * A ResultSetMetaData which contains meta information about all columns for the table. * @param columnDefaultValues * A map of possibly defined values by default for columns. * @param i * Index of column which should be constructed. * @return A constructed ColumnMetaData object. * @throws SQLException * Is thrown in case any errors during work with database occur. */ private ColumnMetaData getColumnMetaData(ResultSetMetaData rsmd, Map<String, String> columnDefaultValues, int i) throws SQLException { SqlTypes columnType = SqlTypes.getSqlTypeByJdbcSqlType(rsmd.getColumnType(i)); ColumnMetaData column = ColumnMetaData.getInstance(rsmd.getColumnName(i), columnType) .setNullable(rsmd.isNullable(i) == ResultSetMetaData.columnNullable) .setAutoincrement(rsmd.isAutoIncrement(i)); if (columnDefaultValues.containsKey(rsmd.getColumnName(i))) { column.setDefaultValue(columnDefaultValues.get(rsmd.getColumnName(i))); } if (columnType.isHasSize()) { column.setSize(rsmd.getColumnDisplaySize(i)); } return column; } /** * Gets a default values for each of the columns if that values are defined for the columns. * * @param dbMetaData * a DatabaseMetaData instance to fetch the information from. * @return A map where key is a Column name and value is Column's default. * @throws SQLException * Is thrown in case any errors during work with database occur. */ private Map<String, String> getColumnDefaults(final DatabaseMetaData dbMetaData) throws SQLException { Map<String, String> columnDefaultValues = new HashMap<String, String>(); ResultSet tableMetaData = null; try { tableMetaData = dbMetaData.getColumns(null, null, tableName, "%"); while (tableMetaData.next()) { String defaultValue = tableMetaData.getString("COLUMN_DEF"); if (defaultValue != null && defaultValue.length() > 0) { columnDefaultValues.put(tableMetaData.getString("COLUMN_NAME"), defaultValue); } } } finally { if (tableMetaData != null) { tableMetaData.close(); } } return columnDefaultValues; } private final String tableName; private final DataSource dataSource; private final JdbcTemplate jdbcTemplate; private static final String SELECT_FROM = "SELECT * FROM "; }