Talk About Network

Google


Register and Login
Nick
Password
Register create new account Sign up is FREE and you can post replies, new topics, bookmark posts and more!
Recover lost password


Programming > Java Databases > Re: Class desig...
Latest [ Topics | Posts ] Archive Post A New Topic Post a Reply
<< Topic < Post Post 2 of 7 Topic 3595 of 3876
Post > Topic >>

Re: Class design for DB code

by Andrey Ryabov <andrey_ryabov@[EMAIL PROTECTED] > Nov 17, 2007 at 11:42 PM

Consider aproach like the following:


package ru.factor.toolkit.jdbc;

im****t java.sql.CallableStatement;
im****t java.sql.Connection;
im****t java.sql.PreparedStatement;
im****t java.sql.ResultSet;
im****t java.sql.SQLException;
im****t java.sql.Statement;
im****t java.util.ArrayList;
im****t java.util.List;

im****t javax.sql.DataSource;

im****t org.apache.commons.lang.StringUtils;
im****t org.apache.commons.logging.Log;
im****t org.apache.commons.logging.LogFactory;

/**
 * Contains convenient methos for working with JDBC.  <u>This class it
not thread safe</u>
 * because it keeps created connection in instance variable during
call of any method.
 * It made for performance resasons. Parameters to stored procedures
and functions passing as
 * <code>java.lang.Object[][]</code>, where each row represent one
paramter.
 * Each paramter is array of three objects (<code>java.lang.Object[]</
code>):
 * <ul><li>parameter's value <code>java.lang.Object</code>.
 * <li>type of paramter {@[EMAIL PROTECTED]
 ParamType}  (<code>ParamType.IN</code>
by default).
 * <li>type of value of parameter {@[EMAIL PROTECTED]
 JDBCType}.
 * </ul>
 * The order is not relevant. Which of elements is required depends on
usage.
 * The main rule is that {@[EMAIL PROTECTED]
 JDBCType}
 * can be omitted when it can be deterimined by class of value.
Therefore if value is <code>null</code> or
 * parameter is <code>OUT</code> you must specify {@[EMAIL PROTECTED]
 JDBCType}
explicitly.
 * <p/>
 * For example:
 * <pre>
 *     new Object[][] {{null, JDBCType.INT}, {"string_value"}}
 * <p/>
 *   Equals to:
 * <p/>
 *     new Object[][] {{null, ParamType.IN, JDBCType.INT},
{"string_value". ParamType.IN, JDBCType.STRING}}
 * <p/>
 *   And can be passed to function with signatue:
 * <p/>
 *    function someFunction(i number, s varchar) return someType;
 * </pre>
 *
 * @[EMAIL PROTECTED]
 Andrey Ryabov
 */
public class JDBCTemplates {
    private static final Log log =
LogFactory.getLog(JDBCTemplates.class);
    private static final int MAX_SQL_LENGTH = 100;
    private DataSource dataSource;
    private Connection connection;
    private ConnectionListener listener;
    private int counter;

    private static final ConnectionListener NULL_LISTENER = new
ConnectionListener() {
        public void afterCreate(Connection connect) {
            // do nothing
        }

        public void beforeClose(Connection connect) {
            // do nothing
        }
    };

    /**
     * Intitializes by <code>DataSource</code>
     *
     * @[EMAIL PROTECTED]
 dataSource
     */
    public JDBCTemplates(DataSource dataSource) {
        this(dataSource, NULL_LISTENER);
    }

    /**
     * Intitializes by <code>DataSource</code>
     *
     * @[EMAIL PROTECTED]
 dataSource
     * @[EMAIL PROTECTED]
 listener
     */
    public JDBCTemplates(DataSource dataSource, ConnectionListener
listener) {
        this.dataSource = dataSource;
        this.listener = listener;
    }

    public static JDBCTemplates createNonThreadSafe(DataSource
dataSource) {
    	return new JDBCTemplates(dataSource);
    }

    public static JDBCTemplates createThreadSafe(final DataSource
dataSource) {
    	return new JDBCTemplates(dataSource) {
    	    public <T, E extends Exception> T
execute(ConnectionHandler<T, E> handler) throws E {
    	    	Connection connection = null;
    	        try {
	                connection = dataSource.getConnection();
    	            return handler.onConnection(connection);
    	        } catch (SQLException ex) {
    	            log.error("Error due to access to database", ex);
    	            throw new DataAcces***ception("Error due to access to
database: ", ex);
    	        } finally {
	                closeNoEx(connection);
    	        }
    	    }
    	};
    }


    /**
     * Prepares connection for working.
     * Gets connection from DataSource, pass it to {@[EMAIL PROTECTED]
(java.sql.Connection)}
     * and close connection.
     *
     * @[EMAIL PROTECTED]
 handler Handler of <code>connection</code>
     * @[EMAIL PROTECTED]
 result of processing if present
     */
    public <T, E extends Exception> T execute(ConnectionHandler<T, E>
handler) throws E {
        try {
            if (counter++ == 0) {
                connection = dataSource.getConnection();
                listener.afterCreate(connection);
            }
            return handler.onConnection(connection);
        } catch (SQLException ex) {
            log.error("Error due to access to database", ex);
            throw new DataAcces***ception("Error due to access to
database: ", ex);
        } finally {
            if (--counter == 0) {
                try {
                    listener.beforeClose(connection);
                } catch (SQLException e) {
                    log.error("Error in ConnectionListener", e);
                }
                closeNoEx(connection);
                connection = null;
            }
        }
    }

    /**
     * Manage <code>PreparedStatement</code> creation and handling.
Gets <code>PreparedStatement</code> via
     * {@[EMAIL PROTECTED]
(java.sql.Connection)},
     * pass it to {@[EMAIL PROTECTED]
(java.sql.PreparedStatement)},
close it.
     *
     * @[EMAIL PROTECTED]
 creator PreparedStatement abstract factory
     * @[EMAIL PROTECTED]
 handler PreparedStatement handler.
     * @[EMAIL PROTECTED]
 result of processing if present
     */
    public <T, E extends Exception> T execute(final
PreparedStatementCreator creator,
                                              final
PreparedStatementHandler<T, E> handler) throws E {
        return execute(new ConnectionHandler<T, E>() {
            public T onConnection(Connection connection) throws E,
SQLException {
                PreparedStatement statement =
creator.createPreparedStatement(connection);
                try {
                    return handler.onPreparedStatement(statement);
                } finally {
                    closeNoEx(statement);
                }
            }
        });
    }

    /**
     * Creates initialized prepared statement. Creates
<code>PreparedStatement</code>, initialize it by parameters
     * and pass it to {@[EMAIL PROTECTED]
(java.sql.PreparedStatement)},
close it.
     *
     * @[EMAIL PROTECTED]
 sql     SQL query string. It can be select, update,
delete.
     * @[EMAIL PROTECTED]
 params  array with statement parameters.
     * @[EMAIL PROTECTED]
 handler PreparedStatement handler.
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T, E extends Exception> T execute(final String sql, final
Object[][] params,
                                              final
PreparedStatementHandler<T, E> handler) throws E {
        return execute(new PreparedStatementCreator() {
            public PreparedStatement
createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement statement =
connection.prepareStatement(sql);
                fillPreparedStatement(statement, params, 1);
                return statement;
            }
        }, handler);
    }

    /**
     * Creates uninitialized prepared statement.
     */
    public <T, E extends Exception> T execute(final String sql,
    			final PreparedStatementHandler<T, E> handler) throws E {
        return execute(new PreparedStatementCreator() {
            public PreparedStatement
createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement statement =
connection.prepareStatement(sql);
                return statement;
            }
        }, handler);
    }


    /**
     * Manage <code>ResultSet</code> creation and handling. Gets
<code>PreparedStatement</code> via
     * {@[EMAIL PROTECTED]
(java.sql.Connection)},
     * invokes <code>PreparedStatement.executeQuery()</code> and pass
result set to
     * {@[EMAIL PROTECTED]
 ResultSetHandler#onResultSet(java.sql.ResultSet)}, close
it.
     *
     * @[EMAIL PROTECTED]
 creator <code>PreparedStatement</code> abstract factory.
     * @[EMAIL PROTECTED]
 handler <code>ResultSet</code> handler.
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T, E extends Exception> T execute(PreparedStatementCreator
creator,
                                              final
ResultSetHandler<T, E> handler)
            throws E {
        return execute(creator, new PreparedStatementHandler<T, E>() {
            public T onPreparedStatement(PreparedStatement
preparedStatement) throws E, SQLException {
                ResultSet resultSet =
preparedStatement.executeQuery();
                try {
                    return handler.onResultSet(resultSet);
                } finally {
                    closeNoEx(resultSet);
                }
            }
        });
    }

    /**
     * Manage <code>CallableStatement</code> creation and handling.
Creates <code>CallableStatement</code> by
     * {@[EMAIL PROTECTED]
(java.sql.Connection)},
     * pass it to {@[EMAIL PROTECTED]
(java.sql.CallableStatement)},
close it.
     *
     * @[EMAIL PROTECTED]
 creator <code>CallableStatement</code> abstract factory.
     * @[EMAIL PROTECTED]
 handler <code>CallableStatement</code> handler
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T, E extends Exception> T execute(final
CallableStatementCreator creator,
                                              final
CallableStatementHandler<T, E> handler) throws E {
        return execute(new ConnectionHandler<T, E>() {
            public T onConnection(Connection connection) throws E,
SQLException {
                CallableStatement statement =
creator.createCallableStatement(connection);
                try {
                    return handler.onCallableStatement(statement);
                } finally {
                    closeNoEx(statement);
                }
            }
        });
    }

    /**
     * Performs select query. Executes select statement and pass
<code>ResultSet</code> to
     * {@[EMAIL PROTECTED]
 ResultSetHandler#onResultSet(java.sql.ResultSet)}
     *
     * @[EMAIL PROTECTED]
 querySQL select query.
     * @[EMAIL PROTECTED]
 params   query parametes.
     * @[EMAIL PROTECTED]
 handler  <code>ResulSet</code> handler.
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T, E extends Exception> T executeSelect(final String
querySQL, final Object[][] params,
                                                    final
ResultSetHandler<T, E> handler) throws E {
        return execute(new PreparedStatementCreator() {
            public PreparedStatement
createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement statement =
connection.prepareStatement(querySQL);
                fillPreparedStatement(statement, params, 1);
                return statement;
            }
        }, handler);
    }

    /**
     * Performs select query. Executes select statement and pass
<code>ResultSet</code> to
     * {@[EMAIL PROTECTED]
 ResultSetHandler#onResultSet(java.sql.ResultSet)}
     */
    public <T, E extends Exception> T executeSelect(final String
querySQL, final int rsType,
            										final int rsConcurrency, final int
rsHoldability,
            										final Object[][] params,
            final ResultSetHandler<T, E> handler) throws E {
        return execute(new PreparedStatementCreator() {
            public PreparedStatement
createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement statement =
connection.prepareStatement(querySQL, rsType,
                		rsConcurrency, rsHoldability);
                fillPreparedStatement(statement, params, 1);
                return statement;
            }
        }, handler);
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #executeSelect(String, Object[][],
ResultSetHandler<T,E>)}
     */
    public <T, E extends Exception> T executeSelect(String querySQL,
ResultSetHandler<T, E> handler,
                                                    Object... params)
throws E {
        return executeSelect(querySQL, convertParams(params),
handler);
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #executeSelect(String, int, int, int,
Object[][], ResultSetHandler)}
     */
    public <T, E extends Exception> T executeSelect(String querySQL,
int rsType, int rsConcurrency,
    			int rsHoldability, ResultSetHandler<T, E> handler, Object...
params) throws E {
        return executeSelect(querySQL, rsType, rsConcurrency,
rsHoldability,
        		convertParams(params), handler);
    }


    /**
     * Returns first column of queries result as List. You should use
appropriate
     * {@[EMAIL PROTECTED]
 JDBCTypeConverter}
     * to convert from JDBC type to type you needed.
     *
     * @[EMAIL PROTECTED]
 querySQL select query.
     * @[EMAIL PROTECTED]
 params   parameters of query.
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T> List<T> selectForList(String querySQL, final
JDBCTypeConverter converter, Object[][] params) {
        return executeSelect(querySQL, params, new
ResultSetHandler<List<T>, DataAcces***ception>() {
            @[EMAIL PROTECTED]
("unchecked")
            public List<T> onResultSet(ResultSet resultSet) throws
SQLException {
                List<T> result = new ArrayList<T>();
                while (resultSet.next()) {
                    result.add((T)
converter.convertFromJDBC(resultSet.getObject(1)));
                }
                return result;
            }
        });
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #selectForList(String, JDBCTypeConverter,
Object[][])}
     */
    public <T> List<T> selectForList(String querySQL, Object...
params) {
        return selectForList(querySQL,
JDBCTypeConverters.NO_CONVERSION, convertParams(params));
    }

    /**
     * Returns first column of first row of result as Object. You
should use appropriate
     * {@[EMAIL PROTECTED]
 JDBCTypeConverter}
     * to convert from JDBC type to type you needed.
     *
     * @[EMAIL PROTECTED]
 querySQL select query.
     * @[EMAIL PROTECTED]
 params   query parameters.
     * @[EMAIL PROTECTED]
 result of processing if present.
     */
    public <T> T selectForObject(final String querySQL, final
JDBCTypeConverter converter, Object[][] params) {
        return executeSelect(querySQL, params, new ResultSetHandler<T,
DataAcces***ception>() {
            @[EMAIL PROTECTED]
("unchecked")
            public T onResultSet(ResultSet resultSet) throws
SQLException {
                if (!resultSet.next()) {
                    throw new NoDataFoundException("One row required
for query: " + querySQL);
                }
                return (T)
converter.convertFromJDBC(resultSet.getObject(1));
            }
        });
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #selectForObject(String, JDBCTypeConverter,
Object[][])}
     */
    @[EMAIL PROTECTED]
("unchecked")
    public <T> T selectForObject(String querySQL, Object... params) {
        return (T) selectForObject(querySQL,
JDBCTypeConverters.NO_CONVERSION, convertParams(params));
    }

    /**
     * Performs update query and returns number of rows updated.
     *
     * @[EMAIL PROTECTED]
 updateSQL update query.
     * @[EMAIL PROTECTED]
 params    query parameters.
     * @[EMAIL PROTECTED]
 number of rows updated.
     */
    public int executeUpdate(final String updateSQL, Object[][]
params) {
        return execute(updateSQL, params, new
PreparedStatementHandler<Integer, DataAcces***ception>() {
            public Integer onPreparedStatement(PreparedStatement
preparedStatement) throws SQLException {
                return new Integer(preparedStatement.executeUpdate());
            }
        });
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #executeUpdate(String, Object[][])}
     */
    public int executeUpdate(String updateSQL, Object... params) {
        return executeUpdate(updateSQL, convertParams(params));
    }

    /**
     * Performs insert query and returns object generated identity
object.
     *
     * @[EMAIL PROTECTED]
 insertSQL update query.
     * @[EMAIL PROTECTED]
 params    query parameters.
     * @[EMAIL PROTECTED]
 generated identity object.
     */
    public Object executeInsert(final String insertSQL, final Object[]
[] params) {
        return execute(new PreparedStatementCreator() {
	            public PreparedStatement
createPreparedStatement(Connection connection) throws SQLException {
	                PreparedStatement statement =
connection.prepareStatement(insertSQL,
Statement.RETURN_GENERATED_KEYS);
	                fillPreparedStatement(statement, params, 1);
	                return statement;
	            }},
        	new PreparedStatementHandler<Object, DataAcces***ception>() {
	            public Object onPreparedStatement(PreparedStatement
preparedStatement) throws SQLException {
	                preparedStatement.executeUpdate();
	                ResultSet rSet =
preparedStatement.getGeneratedKeys();
	                try {
	                    return rSet.next() ? rSet.getObject(1) : null;
	                } finally {
	                    JDBCTemplates.closeNoEx(rSet);
	                }
	            }});
    }

    /**
     * Shortcut for {@[EMAIL PROTECTED]
 #executeInsert(String, Object[][])}
     */
	public Object executeInsert(String insertSQL, Object... params) {
        return executeInsert(insertSQL, convertParams(params));
    }

    /**
     * Executes SQL statement.
     *
     * @[EMAIL PROTECTED]
 statementSQL sql statemenet
     * @[EMAIL PROTECTED]
 same value as {@[EMAIL PROTECTED]
(String)}
     * @[EMAIL PROTECTED]
 {@[EMAIL PROTECTED]
 java.sql.Statement#execute(String)}
     */
    public boolean executeStatement(final String statementSQL, final
Object... params) {
        return execute(new ConnectionHandler<Boolean,
DataAcces***ception>() {
            public Boolean onConnection(Connection connection) throws
SQLException {
                PreparedStatement statement =
connection.prepareStatement(statementSQL);
                fillPreparedStatement(statement,
convertParams(params), 1);
                return statement.execute();
            }
        });
    }

    /**
     * Performs stored function call. Call stored function, gets
return value convert it by
     * {@[EMAIL PROTECTED]
 JDBCTypeConverter}
     * from {@[EMAIL PROTECTED]
 JDBCType}.
     *
     * @[EMAIL PROTECTED]
 functionSQL query.
     * @[EMAIL PROTECTED]
 returnType  type of expected result.
     * @[EMAIL PROTECTED]
 params      params of function.
     * @[EMAIL PROTECTED]
 function's return value.
     */
    public <T> T functionCall(final String functionSQL, final JDBCType
returnType, final Object[][] params) {
        return execute(new CallableStatementCreator() {
            public CallableStatement
createCallableStatement(Connection connect) throws SQLException {
                CallableStatement call =
connect.prepareCall(functionSQL);
                returnType.registerReturnParameter(call, 1);
                fillPreparedStatement(call, params, 2);
                return call;
            }
        }, new CallableStatementHandler<T, DataAcces***ception>() {
            @[EMAIL PROTECTED]
("unchecked")
            public T onCallableStatement(CallableStatement call)
throws SQLException {
                call.execute();
                return (T) returnType.getReturnParameter(call, 1);
            }
        });
    }

    /**
     * Performs call of stored procedure. If parameter is marked as
<code>OUT</code> ou <code>IN_OUT</code>
     * it's value will be set in result of stored procedure. If {@[EMAIL PROTECTED]
     * is <code>OUT<code> or <code>IN_OUT</code> and its {@[EMAIL PROTECTED]
 can't be
     * determined, the type must be specified explicitly.
     *
     * @[EMAIL PROTECTED]
 procedureSQL query string.
     * @[EMAIL PROTECTED]
 params       parameters of procedure.
     * @[EMAIL PROTECTED]
 same array as passed to method.
     */
    public Object[][] procedureCall(final String procedureSQL, final
Object[][] params) {
        return execute(new CallableStatementCreator() {
            public CallableStatement
createCallableStatement(Connection connect) throws SQLException {
                CallableStatement call =
connect.prepareCall(procedureSQL);
                fillPreparedStatement(call, params, 1);
                registerOutputParams(call, params);
                return call;
            }
        }, new CallableStatementHandler<Object[][],
DataAcces***ception>() {
            public Object[][] onCallableStatement(CallableStatement
call) throws SQLException {
                call.execute();
                fillOutputParams(call, params);
                return params;
            }
        });
    }

    /**
     * Deletes white spaces and abbreviate query. Used in proposes of
logging.
     */
    public static String formatSQL(String originalSQL) {
        return formatSQL(originalSQL, MAX_SQL_LENGTH);
    }

    /**
     * Deletes white spaces and abbreviate query. Used in proposes of
logging.
     *
     * @[EMAIL PROTECTED]
 originalSQL  original query text.
     * @[EMAIL PROTECTED]
 maxSqlLength maximal length of formated query.
     */
    public static String formatSQL(String originalSQL, int
maxSqlLength) {
        String[] clauses = StringUtils.split(originalSQL);
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < clauses.length; i++) {
            if (i != 0) {
                result.append(" ");
            }
            result.append(clauses[i]);
        }
        return StringUtils.abbreviate(result.toString(),
maxSqlLength);
    }

    public static void fillOutputParams(CallableStatement call,
Object[][] params) throws SQLException {
        for (int i = 0; i < params.length; i++) {
            ParamType paramType = extractParamType(params[i]);
            if (paramType != ParamType.IN) {
                JDBCType jdbcType = extractJDBCType(params[i]);
                params[i] = new Object[]
{jdbcType.getReturnParameter(call, i + 1), jdbcType, paramType};
            }
        }
    }

    public static void fillPreparedStatement(PreparedStatement call,
final Object[][] params, int startWith)
            throws SQLException {
        for (int i = 0; i < params.length; i++) {
            ParamType paramType = extractParamType(params[i]);
            if (paramType != ParamType.OUT) {
                extractJDBCType(params[i]).setParameter(call,
startWith + i, extractValue(params[i]));
            }
        }
    }

    private void registerOutputParams(CallableStatement call, final
Object[][] params) throws SQLException {
        for (int i = 0; i < params.length; i++) {
            ParamType paramType = extractParamType(params[i]);
            if (paramType != ParamType.IN) {
 
extractJDBCType(params[i]).registerReturnParameter(call, i + 1);
            }
        }
    }

    private static JDBCType extractJDBCType(Object[] param) {
        for (Object p : param) {
            if (p instanceof JDBCType) {
                return (JDBCType) p;
            }
        }
        Object paramValue = extractValue(param);
        if (paramValue != null) {
            return JDBCType.defineType(paramValue);
        }
        return JDBCType.NULL;
    }

    private static Object extractValue(Object[] param) {
        for (Object p : param) {
            if (!(p instanceof JDBCType) && !(p instanceof ParamType))
{
                return p;
            }
        }
        log.error("Value in param wasn't found: " + param);
        throw new IllegalArgumentException("There is no value in
param: " + param);
    }

    private static ParamType extractParamType(Object[] param) {
        for (Object p : param) {
            if (p instanceof ParamType) {
                return (ParamType) p;
            }
            if (p instanceof ResultSetHandler) {
                return ParamType.OUT;
            }
        }
        return ParamType.IN;
    }

    public static Object[][] convertParams(Object[] params) {
        Object[][] result = new Object[params.length][];
        for (int i = 0; i < params.length; i++) {
            result[i] = new Object[]{params[i]};
        }
        return result;
    }

    public static void closeNoEx(Connection connection) {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            log.error("Error closing connection", e);
        }
    }

    public static void closeNoEx(PreparedStatement statement) {
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
            log.error("Error closing prepared statement", e);
        }
    }

    public static void closeNoEx(ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
            log.error("Error closing result set", e);
        }
    }
}
 




 7 Posts in Topic:
Class design for DB code
"Durango2008" &  2007-11-18 05:27:23 
Re: Class design for DB code
Andrey Ryabov <andrey_  2007-11-17 23:42:18 
Re: Class design for DB code
David Harper <devnull@  2007-11-18 07:54:55 
Re: Class design for DB code
Andrey Ryabov <andrey_  2007-11-17 23:55:20 
Re: Class design for DB code
Andrey Ryabov <andrey_  2007-11-18 00:14:07 
Re: Class design for DB code
Doug Morse <morse@[EMA  2007-11-20 13:24:29 
Re: Class design for DB code
Andrey <andrey_ryabov@  2007-11-30 07:40:05 

Post A Reply:
  Go here to Signup

AddThis Feed Button


About - Advertising - Contact - Frequently Asked Questions - Privacy Policy - Terms of Use - Signup

Contact
tan12V112 Thu Dec 4 0:05:00 CST 2008.