jueves, 5 de mayo de 2011

Query's dinamicos.

En esta ocasión les muestro un código que sirve para crear un preparedStatement con valores de búsqueda variables, estos se pueden agregar a voluntad con la interface TreeMap , esto permite generar query's de manera dinámica a la hora de hacer consultas, pudiendo poner uno o mas parámetros sin tener que escribir excesivo código con if-else a la hora de programar, esto nos permite hacer menos código a la hora de programar busquedas en sql, y combinar criterios sin tener que pelear demasiado a la hora de codificar, el resultado del método es una sentencia preparada. la cual solo necesita ser ejecutada y para regresar el set de resultados.



/**
* Creates a Prepared Statement Query using variable arguments for a custom search,
* if the query has no arguments or values the query remains
* the same and no search params are added, this allows to make a custom
* search without having to add a lot of if-else statements
* @param baseQuery a database query that contains no search parameters
* @param queryParams search parameters which are pairs that contain criteria and values​​, which are added to the database query to generate a ready to run PreparedStatement
* @param connection connection to the database open and ready for querys
* @return returns a PreparedStatement with variable search parameters to get data ready to run
*/
public PreparedStatement setQueryParams(String baseQuery, TreeMap<String, Object> queryParams,
Connection connection) throws SQLException {
StringBuilder query = new StringBuilder(baseQuery);
PreparedStatement preparedStatement = null;
Object[] keysObject = queryParams.keySet().toArray();
/*we check the parameters size and if the hashmap is not empty we begin
to build a query using the keys for storing the values */
if (queryParams.size() > 0) {
boolean first = true;
for (Object key : keysObject) {
if (first) {
/*if this is the first value we add the clause "where"
and set first to false so we can add the "and" clauses*/
query.append(" where ");
first = false;
} else {
query.append(" and ");
}
query.append( (String) key + " = ? ");
}
}
/*we create a prepared statement ready for accepting parameters*/
preparedStatement = connection.prepareStatement(query.toString());
/*here we iterate through the values in the set and put the corresponding
values for each one of them in the prepared statement and add them as a
correct Object , the corresponding values equals the current keyset
order since the treemap uses the same order for the keys*/
for (int i = 0; i < keysObject.length; i++) {
String key = (String) keysObject[i];
Object value = queryParams.get(key);
if (value instanceof Integer) {
preparedStatement.setInt(i + 1, (Integer) value);
} else if (value instanceof Float) {
preparedStatement.setFloat(i + 1, (Float) value);
} else if (value instanceof Double) {
preparedStatement.setDouble(i + 1, (Double) value);
} else if (value instanceof String) {
preparedStatement.setString(i + 1, (String) value);
} else if (value instanceof Date) {
long time = ((Date) value).getTime();
preparedStatement.setDate(i + 1, new java.sql.Date(time));
}
}
/*finally we return the prepared statement with all values set*/
return preparedStatement;
}

martes, 3 de mayo de 2011

como no debe hacerse un query.


En esta ocasión podemos analizar otra pieza de código la cual requiere un análisis para determinar los problemas que ocasiona programar de esta manera. este es otro ejemplo de código el cual tiene practicas que impiden que la velocidad de ejecución sea óptima.








/**
* Fills the PreparedStatement with its respective values
* @param pstmt
* @param request
* @return
*/
public PreparedStatement setQueryvalues(PreparedStatement pstmt, HttpServletRequest request) {
try {
String search = request.getParameter("search") != null ? request.getParameter("search") : "";
log.debug("search = " + search);
//Check if there is any filter to search
if (search.equals("search")) {
String lang = request.getParameter("lang") == null ? "" : request.getParameter("lang");
String pid = request.getParameter("id") == null ? "" : request.getParameter("id");
String pname = request.getParameter("name") == null ? "" : request.getParameter("name");
log.debug("lang --> " + lang);
log.debug("pid --> " + pid);
log.debug("name --> " + pname);
request.setAttribute("id", pid);
request.setAttribute("name", pname);
request.setAttribute("search", search);
if (pid != null && !pid.equals("")) {
pstmt.setString(1, "%" + pid);
} else {
pstmt.setString(1, "%" + pid);
}
if (pname != null && !pname.equals("")) {
if (lang.equals("")) {
pstmt.setString(2, "%" + pname.toUpperCase() + "%");

/*
en este caso la cadena lang viene con el valor %, y el query viene más o menos así:
select * from tabla where valor1 like ? and valor2 like ? and valor3 like ?
pero que pasa si al parametro le pasamos un wildcard "%"
este queda así:
select * from tabla where valor1 like 'valor1' and valor2 like 'valor2' and valor3 like '%'
lo cual forza al query a regresar todos los valores haciendo una comparación tanto inútil,
esto sobrecarga la base de datos y evita que se optimizen los tiempos de busqueda

el mismo resultado puede lograrse con:
select * from tabla where valor1 like 'valor1' and valor2 like 'valor2'
permitiendo que la base de datos busque solo con los criterios de búsqueda necesarios
evitando así una sobrecarga.

*/

pstmt.setString(3, "%");
} else {
pstmt.setString(2, "%");
pstmt.setString(3, "%" + pname.toUpperCase() + "%");
}
} else {
pstmt.setString(2, "%");
pstmt.setString(3, "%");
}
} else {

/*
en este caso tenemos mas problemas al hacer que todos los parámetros
de búsqueda sean wildcards, esto hace una búsqueda con like por toda la
tabla y si el query trae un join esto alenta los resultados

*/

pstmt.setString(1, "%");
pstmt.setString(2, "%");
pstmt.setString(3, "%");
}

} catch (Exception e) {


/*
este antipatron ha sido explicado anteriormente como ocultamiento de excepciones,
este impide depurar correctamente el código para poder encontrar una excepción caso de ocurrir.

*/

e.printStackTrace();
}
return pstmt;
}