package ddth.dasp.framework.dbc; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JdbcUtils { private static final Pattern PATTERN_PARAMS_PLACEHOLDER = Pattern.compile("\\@\\{([^\\}]+)\\}"); private static final Logger LOGGER = LoggerFactory.getLogger(JdbcUtils.class); /** * Quietly closes JDBC resources. * * @param conn * @param stm * @param rs */ public static void closeResources(Connection conn, Statement stm, ResultSet rs) { try { if (rs != null) { rs.close(); } } catch (Exception e) { LOGGER.warn(e.getMessage(), e); } try { if (stm != null) { stm.close(); } } catch (Exception e) { LOGGER.warn(e.getMessage(), e); } try { if (conn != null) { conn.close(); } } catch (Exception e) { LOGGER.warn(e.getMessage(), e); } } /** * Prepares a SQL statement. * * @param conn * @param sql * @param params * @return * @throws SQLException */ public static PreparedStatement prepareStatement(Connection conn, String sql, Object[] params) throws SQLException { return prepareStatement(conn, sql, params, false); } /** * Prepares a SQL statement. * * @param conn * @param sql * @param params * @param isCallable * specify if a {@link CallableStatement} should be returned * @return * @throws SQLException */ public static PreparedStatement prepareStatement(Connection conn, String sql, Object[] params, boolean isCallable) throws SQLException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Preparing statement [" + sql + "] with arguments: " + params); } PreparedStatement stmt = isCallable ? conn.prepareCall(sql) : conn.prepareStatement(sql); try { if (params != null && params.length > 0) { int index = 1; for (Object param : params) { if (param instanceof String || param instanceof Character) { stmt.setString(index, param.toString()); } else if (param instanceof Integer) { stmt.setInt(index, (Integer) param); } else if (param instanceof Long) { stmt.setLong(index, (Long) param); } else if (param instanceof Float) { stmt.setFloat(index, (Float) param); } else if (param instanceof Double) { stmt.setDouble(index, (Double) param); } else { stmt.setObject(index, param); } index++; } } } catch (Exception e) { if (stmt != null) { stmt.close(); } if (e instanceof SQLException) { throw (SQLException) e; } else { throw new SQLException(e); } } return stmt; } /** * Prepares a SQL statement. * * @param conn * @param sql * @param params * @return * @throws SQLException */ public static PreparedStatement prepareStatement(Connection conn, String sql, Map<String, Object> params) throws SQLException { return prepareStatement(conn, sql, params, false); } /** * Prepares a SQL statement by name. * * @param conn * @param sql * @param params * @param isCallable * specify if a {@link CallableStatement} should be returned * @return the {@link PreparedStatement} ready for execution * @throws SQLException */ public static PreparedStatement prepareStatement(Connection conn, String sql, Map<String, Object> params, boolean isCallable) throws SQLException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Preparing statement [" + sql + "] with arguments: " + params); } String[] paramsByIndex = extractParams(sql); List<Object> paramsValueByIndex = new LinkedList<Object>(); for (String paramName : paramsByIndex) { if (params != null && params.containsKey(paramName)) { paramsValueByIndex.add(params.get(paramName)); } else { throw new SQLException("Missing value for parameter " + paramName); } } String cleanSql = PATTERN_PARAMS_PLACEHOLDER.matcher(sql).replaceAll("?"); return prepareStatement(conn, cleanSql, paramsValueByIndex.toArray(), isCallable); } /** * Extracts parameters from a SQL statement and preserves theirs index. * * For example: with the SQL statement * <code>INSERT INTO user (id, email, password) VALUES (${id}, ${email}, ${password})</code> * , the method will return <code>["id", "email", "password"]</code> * * @param sql * @return */ public static String[] extractParams(String sql) { List<String> result = new LinkedList<String>(); Matcher matcher = PATTERN_PARAMS_PLACEHOLDER.matcher(sql); while (matcher.find()) { result.add(matcher.group(1)); } return result.toArray(new String[0]); } }