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;
}



sábado, 30 de abril de 2011

malas practicas del codigo

Malas Practicas de Código


esta es la segunda entrada, en esta analizaremos de nueva cuenta código
con malas practicas y que puede ser mejorado, recuerda que las buenas
practicas de programación mejoran la calidad del programa y disminuyen
el tiempo correcciones




/*Hay que ser mas descriptivo cuando se genera un JavaDoc, recuerda que eso ayuda al momento de la documentaciel sistema.*/
/**
* Generates the paginated list
* @param query
* @param numPagina
* @param numRegPagina
* @param hmPaging
* @param request
* @return
*/
/*Los nombres de los parametros son espaingles, lo mejor es ser
coherente y manejar todo en un solo idioma.*/

public ArrayList getPagedList(String query, int numPagina, int numRegPagina, HashMap hmPaging, HttpServletRequest request) {
ArrayList list = new ArrayList();
ResultSet rs = null;
try {
this.openConecction();
PreparedStatement pstmt;
pstmt = con.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

/*No es Correcto usar Scroll Insensitive como
Propiedad para generar un ResultSet,Esto Consume mas memoria,
y en un sistema Web la cantidad de Memoria consumida es
Crucial para el buen funcionamiento*/


pstmt = setQueryvalues(pstmt, request);
pstmt.setFetchSize(numRegPagina);
rs = pstmt.executeQuery();
int cont = 1;
int fila = numRegPagina * (numPagina - 1) + 1;
if (rs.absolute(fila) && numRegPagina & gt;
0)
{


/*no es correcto hacer que el ResultSet vaya al numero absoluto,
esto consume una gran cantidad de memoria.*/


do {
Asset asset = new Asset();
asset.setId(new Integer(rs.getInt(arrayAsset[0])));
asset.setId_division(new Integer(rs.getString(arrayAsset[1])));
asset.setName_sp(rs.getString(arrayAsset[2]));
asset.setName_en(rs.getString(arrayAsset[3]));
asset.setStatus(new Integer(rs.getString(arrayAsset[4])));
asset.setUpdated(rs.getString(arrayAsset[5]));
asset.setImage(rs.getString(arrayAsset[6]));
asset.setId_assetType(new Integer(rs.getString(arrayAsset[7])));
list.add(asset);
cont++;
} while (rs.next() && (cont & lt;
= numRegPagina

)); } Integer reccordCount = new Integer(0);


/* El last va al ultimo registro, esto consume una gran cantidad de tiempo y memoria,
lo mejor es siempre dejar que este trabajo sea por parte de la base de datos,
haciendo un segundo query con un count a la base de datos se disminuye este problema */

if (rs.last()) {
reccordCount = new Integer(rs.getRow());
}
log.debug("reccordCount --> " + reccordCount);
hmPaging.put("reccount", reccordCount);


/*Jamas dejen un try y catch sin escalarlo correctamente este
tipo de practica es conocida como ocultamiento de errores ,
lo mejor es escalar la excepción mostrar los detalles y separar las excepciones,
para determinar si fue error del usuario o del programador */

} catch (Exception e) {
e.printStackTrace();
} finally {
this.closeConnection();
}
return (list);

}

sábado, 16 de abril de 2011

Malas Practicas Java (2)


este codigo se llama reemplazaCadena, sin embargo java tiene una utilidad de código el cual es: replace y replaceAll, la cual viene como parte de la especificación java


public static String reemplazaCadena(String texto, String patron,
String nuvo_patron) {
int posicion_del_ultimo_patron = 0, posicion_de_un_nuevo_patron = 0;
int longitud_del_patron = patron.length();
StringBuffer result = new StringBuffer();
if (texto == null || texto.equals("") || patron == null || patron.equals("") || nuvo_patron == null) {
return null;
}
while ((posicion_de_un_nuevo_patron = texto.indexOf(patron,
posicion_del_ultimo_patron)) >= 0) {
result.append(texto.substring(posicion_del_ultimo_patron,
posicion_de_un_nuevo_patron));
result.append(nuvo_patron);
posicion_del_ultimo_patron = posicion_de_un_nuevo_patron + longitud_del_patron;
}
result.append(texto.substring(posicion_del_ultimo_patron));
return result.toString();

}






Este antipatrón se llama Reinventar la rueda , un análisis de la documentación puede ahorrar tiempo y eliminar este tipo de problemas a la hora de programar.

Malas Practicas Java (1)

MALAS PRACTICAS.

ahora vamos a analizar código, el siguiente es un código extraído de un programador de
una empresa donde estuve, digamos que este programador se adueño del código que no
era propiedad suya,
sin embargo debido a que su orgullo no le permitió preguntar como
era correcto,
siempre ocasiono serios problemas, es mejor preguntar cuando uno este en duda,
o apoyarse de los demas programadores, siempre es mejor perder 2 o 3 minutos que
se traducen en horas ahorradas,
la mejor filosofia es:
si pasan mas de 3o minutos y no encuentras una respuesta por ti mismo:

pregunta...Alguien conocerá la respuesta.



 /*código de struts 1*/
/*si el código es mas de 100 lineas entonces los métodos no están optimizados*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
/*este tipo de mensajes de log debe de ser removido de la version final, esto solo hace que la aplicaciea mas lenta y por lo tanto su uso no es recomendado */
log.debug("Entra a Asset");

HttpSession session = request.getSession();
/*este busca una session del atributo request y pregunta por el perfil, aquí la referencia deberestar dentro de los filtros de sesi no dentro del co, lo cual hace una mala practica de separacion */
Users user = (Users) session.getAttribute("activeUser");
String usrprofile = request.getParameter("usrprofile") != null ? request.getParameter("usrprofile") : "ADMIN";
if (!"USRMX".equals(usrprofile) && !"USRLC".equals(usrprofile) && !"ADMIN".equals(usrprofile) && !"USRSL".equals(usrprofile) && !"MEGAADMIN".equals(usrprofile)) {
response.sendError(425);
}
String userUpdate = "";
/*los cos de los lenguajes no deben manejarse dentro de la acciestos deben manejarse con el uso de locales*/
String lang = request.getParameter("lang") != null ? request.getParameter("lang") : "";
/*si los cos if y else no contienen ejecucion alguna entonces
la manera correcta de proceder es por medio de un if y un parametro negado,
este tipo de practica hace que el sistema haga mas saltos if-else,
lo cual se traduce en impacto al rendimiento */
if ("".equals(lang)) {
} else if (lang.length() == 3 && "_en".equals(lang)) {
} else {
response.sendError(425);
}
if (user == null) {
log.debug("sin sesion");
if (lang.equals("")) {
response.sendError(200);
} else {
response.sendError(201);
}
}

// Colocacion de variables en request
request.setAttribute("usrprofile", usrprofile);
request.setAttribute("lang", lang);
request.setAttribute("section", "admin.lc.catalog.materials.asset");
request.setAttribute("class", CLASS_NAME.toLowerCase());

String mappingForward = "";
AssetForm assetForm = (AssetForm) form;
// Obtiene la url
String requestedUrl = request.getRequestURL().toString();
String optionStr = requestedUrl.substring(
requestedUrl.lastIndexOf("/") + 1, requestedUrl.lastIndexOf("."));
log.debug("opcion Elegida ==> " + optionStr);

// Si es modo administrador
if (requestedUrl.indexOf("admin") > 0) {

// Devuelve la lista de resultados
if (optionStr.equals("assetList")) {
log.debug("entro en assetList");
ArrayList catalogList = getAssetList(request);

// Remueve variables de request
request.removeAttribute("catalogList");
request.removeAttribute("title");
// Coloca variables en request
request.setAttribute("catalogList", catalogList);
request.setAttribute("title", "list");

mappingForward = "success";
} // Muestra el formulario para agregar un asset
else if (optionStr.equals("assetFormAdd")) {

log.debug("entro en assetFormAdd");
ArrayList listAssettype = toolDAO.getAssetTypeList();
Ccprofile profile = (Ccprofile) session.getAttribute("activeProfile");
String activeProfile = profile.getName();
HashMap mapSP = (HashMap) request.getSession().getAttribute("mapSP");
String pos10 = (String) mapSP.get("POS-10");
ArrayList listDivision = null;
if (activeProfile.equalsIgnoreCase(pos10)) {
user = (Users) session.getAttribute("activeUser");
String idDiv = (String) user.getDivisionId().toString();
listDivision = toolDAO.getDivisionListByDiv(idDiv);
} else {
listDivision = toolDAO.getDivisionList();
}
// Remueve variables de request
request.removeAttribute("list_assettype");
request.removeAttribute("list_division");
request.removeAttribute("title");
// Coloca variables en request
request.setAttribute("list_assettype", listAssettype);
request.setAttribute("list_division", listDivision);
request.setAttribute("title", "formAdd");
mappingForward = "success";
} // Muestra el formulario con los datos para editar un registro
else if (optionStr.equals("assetFormEdit")) {
log.debug("entro en assetFormEdit");
ArrayList listAssettype = toolDAO.getAssetTypeList();
Ccprofile profile = (Ccprofile) session.getAttribute("activeProfile");
String activeProfile = profile.getName();
HashMap mapSP = (HashMap) request.getSession().getAttribute("mapSP");
String pos10 = (String) mapSP.get("POS-10");
ArrayList listDivision = null;
if (activeProfile.equalsIgnoreCase(pos10)) {
user = (Users) session.getAttribute("activeUser");
String idDiv = (String) user.getDivisionId().toString();
listDivision = toolDAO.getDivisionListByDiv(idDiv);
} else {
listDivision = toolDAO.getDivisionList();
}
request.setAttribute("list_assettype", listAssettype);
request.setAttribute("list_division", listDivision);

String id = request.getParameter("id");
if (id != null && !id.equals("")) {
Asset asset = getAssetById(request);
request.setAttribute("asset", asset);
}
// Remueve variables de request
request.removeAttribute("title");
// Coloca variables en request
request.setAttribute("title", "formEdit");
mappingForward = "success";
} // Agrega un registro a la tabla de asset
else if (optionStr.equals("assetAdd")) {
log.debug("entro en assetAdd: " + assetForm);
log.debug("Nombre esp: " + assetForm.getName_sp());
String res = "";
PrintWriter writer = response.getWriter();
/*el uso del metodo getfechaActualHoraActual no sigue la notaciamel-case correcta*/
userUpdate = user.getAccount() + " " + Utils.getfechaActualHoraActual();
log.debug("usuario ---> " + userUpdate);
assetForm.setUpdated(userUpdate);
// valida que no este duplicado el registro
/*estas validaciones van en el ActionForm y no en el Action*/
if (!isDuplicated(assetForm)) {
request.setAttribute("title", "add");
// Agrega el registro
res = addReccord(assetForm);
writer.print("ok");
writer.flush();
writer.close();
} else {
request.removeAttribute("title");
request.setAttribute("title", "formAdd");
writer.print("duplicated");
// res = "duplicatedRecord";
}
mappingForward = res;
} // Actualiza el registro en la base de datos
else if (optionStr.equals("assetEdit")) {
log.debug("entro en assetEdit: " + assetForm);
userUpdate = user.getAccount() + " " + Utils.getfechaActualHoraActual();
log.debug("usuario ---> " + userUpdate);
assetForm.setUpdated(userUpdate);
String update = updateAsset(assetForm);
request.setAttribute("title", "edit");
if (update.equals("success")) {
PrintWriter writer = response.getWriter();
writer.print("ok");
writer.flush();
writer.close();
}
mappingForward = "success";
} // Elimina un registro en la base de datos.
else if (optionStr.equals("assetDelete")) {
/*Este flujo if-else no ejecuta nada*/
if (request.getHeader("Referer") != null) {
if (request.getHeader("Referer").toUpperCase().indexOf("KO.COM") > -1 || request.getHeader("Referer").toUpperCase().indexOf("8888") > -1) {
System.out.println("referer ==> " + request.getHeader("Referer"));
String token = request.getParameter("nekoTlaiceps");
/*estos comentarios son indescriptivos y no se recomiendan, la recomendacion es que se indique que valores puede traer la variable y como debe ser tratada cada una de ellas*/
// valida que el token traiga algo
if (token != null && !token.equalsIgnoreCase("")) {
// obtiene el hdiv de session
String hdiv = (String) session.getAttribute("HDIV");
// valida que el token sea igual al hdiv de session
if (hdiv.equalsIgnoreCase(token)) {
log.debug("entro en assetDelete");
String id = request.getParameter("id");
if (id != null && !id.equals("")) {
boolean update = deleteAsset(request);
request.setAttribute("title", "edit");
if (update) {
mappingForward = "success";
} else {
mappingForward = "error";
}
}
} else {
mappingForward = "error";
}
} else {
mappingForward = "error";
}
token = "";
} else {
System.out.println("Error referer no contiene token");
response.sendError(425);
}
} else {
System.out.println("Referer es null ");
response.sendError(425);
}
}
} else { // Si es modo usuario
/*este código es innecesario, debido a que no ejecuta ninguna sentencia*/
if (optionStr.equals("News")) {
}
}

return mapping.findForward(mappingForward);
}







como pueden ver, la acción debió dividirse en varias acciones, permitiendo que los códigos sean mas faciles de entender, esta es una sola acción que pretende meter todo en uno, este tipo de antipatrones se llama God Object.