Usando HashMaps en vez de cursores en BD.
Hola a todos, antes que nada una disculpa por no haber podido hacer acto de presencia en este proyecto, pero por diversas ocupaciones no me fue posible, pero aquí estamos y como diría un buen amigo mió ¡ARRREEE!
Este primer post surgió de un problema en el trabajo con los tiempos de respuesta que nos arrojaba la ejecución de query para un reporte, debido al arduo filtrado que le fue aplicado y a la necesidad de diversos niveles de detalle y agrupaciones, llegamos a un query de 1000 líneas y cerca de 9 horas en ejecución.
Muchos de ustedes se estarán preguntando --Porque diablos no separaron el query y lo convirtieron en un Store Procedure y se quitan de problemas ¿?-- La respuesta es que debido a la premura del tiempo (tomar en cuenta el tiempo que un versionamiento a producción implica en ambientes de alto control), el hecho de que el query ya estaba armado, probado, y que en ocasiones o no se tiene el conocimiento de PL/SQL o no se tienen los permisos correspondientes. Así que tras cavilarlo un rato nos decidimos a resolver el problema desde java.
Usamos una objeto del tipo colección mas concretamente un HashMap el cual básicamente esta compuesto por dos campos, una llave y un campo valor es un objeto indexado lo cual lo hace extremadamente rápido al realizar consultas a sus registros, de esta manera podemos colocar el contenido de una tabla, consulta, vista lo que se les ocurra en su interior y mantener la capacidad de acceder a los datos de una manera eficiente, almacenando nuestra(s) columna(s) llave dentro de su homónimo en el HM y el resto de la columnas dentro de una Lista en el campo valor del buen HM.
Ahora tenemos un objeto con capacidad de almacenar nuestras consultas y acceder rápidamente a sus datos ¿y?
Pues bien, volvamos a nuestro query, al ser un reporte que requiere un nivel de detalle muy grande, involucro acceder varias veces al mismo universo de datos mediante subquerys de esta manera al efectuar full scans entre los mismos datos varias veces el tiempo se nos disparo muchísimo así que dividimos el query en tres etapas donde en primer lugar se extraen tos datos, en según se aplicaban filtros y en la tercer etapa se aplican subtotales y totales. Cada una de las divisiones antes mencionadas fue almacenada en un HM.
Teniendo los tres HM llenos procedemos mediante iteraciones como en el siguiente ejemplo a cruzar la información y filtrar los datos:
final Set set = conceptoABHM.entrySet();
final Iterator iterator = set.iterator();
lstRegistro = new ArrayList();
while(iterator.hasNext()) {
final Object[] borderosA = new Object[columnas];
final Map.Entry entry = (Map.Entry) iterator.next();
if(conceptoCHM.containsKey(entry.getKey())) {
lstDetalleColumnas = new ArrayList();
lstDetalleColumnas = (List) entry.getValue();
lstDetalleColumnas.add((String) conceptoCHM.get(entry.getKey()));
borderosA[0] = lstDetalleColumnas.get(0);
borderosA[1] = lstDetalleColumnas.get(1);
borderosA[2] = lstDetalleColumnas.get(2);
borderosA[3] = lstDetalleColumnas.get(3);
borderosV.add(borderosA);
}
}
Esto seria más o menos el equivalente java de un cursor en PL/SQL, con el pequeño de detalle que lo hace de un 15 a 20 % mas rápido, mayormente debido a la arquitectura propia del sistema, teniendo como primer punto a favor la indexación de los HM, y en segundo término se evita la petición de ejecución Oracle y los tiempos de comunicación derivados de ella.
Espero sus comentarios, preguntas o sugerencias.
Salu2!!
- SiN's blog
- Inicie sesión o regístrese para enviar comentarios
Uso de Memoria y otras cosas...
Qué tan escalable es tu solución? Qué pasa si en vez de mil registros obtienes un millón?
Por cierto, tienes tu variable lstDetalleColumnas, le asignas un ArrayList vacio e inmediatamente despues le asignas la lista que viene como valor de la entrada actual del mapa. Estas creando listas vacias innecesariamente.
Luego, el arreglo borderosA guarda cuatro objetos, todos provenientes del valor del mapa, que al parecer trae tres objetos y le agregas un cuarto que es un concepto que viene de otro mapa bajo la misma llave de la entrada actual del mapa que estas recorriendo. No es mas eficiente usar el metodo toArray() en vez de hacer esas cuatro asignaciones? O acaso borderosA mide más de 4 y el resto de los objetos se rellenan posteriormente en codigo que ya no vemos?
Finalmente, por qué guardar un Object[] en borderosV (por la V me imagino que es un vector, no sé si necesitas un Vector o si te sirve un ArrayList... si borderosV no esta bajo riesgo de tener accesos concurrentes, mejor usa un ArrayList porque Vector es leeeeeeeento porque es thread-safe). Por qué no guardas simplemente el ArrayList de lstDetalleColumnas en borderosV? Asi te ahorras el borderosA. Por otra parte, si necesitas que sea un Object[] lo que va en borderosV, entonces mejor usa toArray():
y te ahorras la variable borderosA. O bien, quitas ese
y simplemente haces
porque nada mas estas agregando ese valor a tu lista de lstDetalleColumnas para pasarlo a borderosA en la misma iteracion... a menos que necesites poner ese valor en la lista porque se va a usar despues.
En cuanto a los permisos en Oracle y demas... tal vez sea una manera de darle la vuelta esto que estas haciendo, pero no hay nada mejor que tener un query optimizado y los indices necesarios en las tablas que estas consultando para acelerar dramaticamente la velocidad de respuesta del query, haciendo innecesario todo ese codigo. Si un requerimiento cambia y resulta que tienes que obtener una columna adicional en el query, vas a tener que cambiar tu codigo en varias partes, no solamente en un lugar, y eso es dificil de depurar si se te va un cambio.
Saludos,
eZL
Given the choice of dancing pigs and security, users will choose dancing pigs, every single time.
Steve Riley
Solucion
Algo parecido hice yo, realize un framework llamado arena framework trabaja con persistencia.
por otro lado por ahi tienen razon con lo del Vector y ArrayList, tambien existe una forma mas rapida de sacar las columnas, pidiendo la metadata del ResultSet, revisa mi framework y comentame que te parece.
Saludos,
Sun Certified Java Developer
Sun Certified Java Programer
Brainbench Certified JavaServer Pages
Arena
Tu framework me recuerda un poco a iBatis pero sin el mapeo en XML. Los ORM's generalmente necesitan una guía de mapeo para saber qué clases generar para cada query; en tu caso te lo estas saltando porque todo se convierte en un DBTable; diría que no es realmente un ORM sino una ayuda para simplificar un poco las consultas y el manejo de base de datos. Lei el tutorial, no vi nada respecto de relaciones maestro-detalle (no recuerdo si iBatis lo maneja; Hibernate y Cayenne sí).
Para simplificar el manejo de base de datos me gusta usar Spring JDBC, que igual le avientas el query y te da una lista de mapas o de alguna otra clase si le das una clase pequeña que mapea las tuplas de la base de datos con objetos de alguna clase. Para actualizar simplemente le pasas el query del update y los valores a llenar en los parámetros. Para saltarte SQL por completo puedes usar iBatis o algún otro framework similar.
Me parece interesante que hayas liberado este código (no vi bajo qué licencia), mi comentario va simplemente a que es un framework más de persistencia de los cuales ya hay bastantes, cada uno con distintas ventajas y características, pero en general hay al menos tres o cuatro que están bastante más avanzados; si bien requieren una configuración inicial, creo que la gente que hace cosas en Java está acostumbrada a manejar XML en cantidades industriales :) Tal vez podrías aprovechar tu talento y tu tiempo en algun proyecto que necesite más de tu ayuda, en vez de crear un framework más de persistencia? (si si, al final quedé como vil criticón porque además no estoy proponiendo uno; hay miles en SourceForge, el dia que busques algo ahi para resolver un problema y lo único que encuentres sea una librería o aplicación a la mitad, ahi es donde puedes ayudar).
Saludos,
eZL
Given the choice of dancing pigs and security, users will choose dancing pigs, every single time.
Steve Riley
Hola, bueno mira, el mapeo
Hola, bueno mira, el mapeo con las dependencias no lo tengo terminado, si tenia la idea que lo hiciera, pq si no, es menos q los frameworks actuales, la licencia la tengo bajo SGPL en java.net esta el proyecto, no le he seguido pq no he tenido tiempo pero espero pronto actualizar los archivos, hice este framework pq ya estaba cansado de que me den una aplicacion con un esquema de base de datos que se supone es el verdadero, y a la final cuando se va liberar la aplicacion en el ambiente real resulta que las tabalas tienen prefijos o sufijos(por "cosas de seguridad" segun los "geeks" de la empresa) y hay que cambiar tanto las clases entidades como las clases del negocio, entonces se me ocurrio hacer esto, algo como la filosofia de ruby sin tener que hacer mapeos o creaciones de clases con sus metodos set y get que si hay cambios grandes de ese tipo solo cambie el query no la aplicacion completa, ya que todos tus entity classes tienes que cambiarles el nombre. Se que hay muchos frameworks de persistencia, pero noc, se me ocurio hacerlo y lo monte. Voy a revisar eso del Spring JDBC pq no lo habia usado.
Gracias por la critica/comentario/sugerencia
:-)
Sun Certified Java Developer
Sun Certified Java Programer
Brainbench Certified JavaServer Pages
Nombres de clases?
Pero qué framework intentaste usar que tenias que cambiar el nombre de la clase (o de una propiedad) si se cambia el nombre de la tabla o columna? Por lo general indicas el nombre de la tabla y la clase a la que se mapea, igual con los nombres de columnas. Por ejemplo en Hibernate por default si le dices nada mas el nombre de la columna, busca una propiedad con el mismo nombre, pero puedes darle el nombre de la columna y la propiedad contra la que lo debe mapear y listo. La idea siempre ha sido tener cierta independencia de la base de datos, y no tener que usar SQL dentro de tu aplicación.
Si la estructura de una base de datos cambia de manera tan drástica que hay que reconstruir el mapa del ORM, seguramente también cambiará la lógica en algunos componentes que lidian directamente con los objetos mapeados de la base de datos. No veo cómo puedas evitar eso con ningún framework.
Suerte con el desarrollo de tu framework; hazle publicidad para que más gente lo conozca y si les resulta útil te ayuden a completarlo y extenderlo. Alguna razón por la que hayas elegido SGPL? no habia oido de esa licencia y la investigué, supongo que estás haciendo algo en J2ME?
Given the choice of dancing pigs and security, users will choose dancing pigs, every single time.
Steve Riley
OK, hibernate no se uso ya
OK, hibernate no se uso ya que el lider del proyecto es de dot net y cuando entre al proyecto se tenia pensado hacer con queries normalitos (old school, cosa que me extraña pq creo q para dot net tambien existe algo como hibernate) y para completar entre ya con el proyecto empesado, ademas como el lider no conocia de esto pues ya que mas... hice mis esfuersos pero no pude hacer funcionar un ejemplo rapido con hibernate(lo necesitaba en 10 min para una reunion) lo que me llevo a usar JPersist pero no les agrado la idea (y eso q JPersist es menos complicacion que hibernate pq no hay mapeo con XML) tonces a la final quedamos con los queries a la vieja escuela y cuando llegamos a la empresa (SURPRISE!!) el cliente "MISTERIOSAMENTE" se le habia "olvidado" decir algunos "detalles" sobre la base de datos....
Con realacion a lo de SGPL no se, se la puse pq me parecio bonito el nombre hahahah no mentira, yo no se mucho de lincencias me parecen extremadamente aburrido tener q leer todo eso para solo decir... usa el codigo como quieras y cuando quieras... ya se que tengo q leer para aprender bien todos los detalles de cualquier cosa, pero cuando la estaba haciendo me dio flojera, SGPL es Sun General Public Licence. cuando registras un proyecto en java.net es una de las opciones.
A la fina se que estoy mal con lo de haber escogido una licencia muy arbitrariamente, pero creo que cada licencia nueva que sacan de OpenSource usan la primera licencia que sacaron y le ponen sinonimos para definir una nueva licencia, tonces me parecio absurdo el evaluar una licencia .. y digamos que callo random en el combobox, medio la lei por encima y asi se quedo (hahaha nuevamente ya se que toy mal al hacer eso pero me dio flojera)
Sun Certified Java Developer
Sun Certified Java Programer
Brainbench Certified JavaServer Pages
NHibernate, licencias
La versión de hibernate para .NET se llama NHibernate, no sé qué tan avanzada vaya, supongo que ya debe ser algo estable en .NET aunque no tenga todos los features que tiene en Java. Y pues lástima que sea tan común tu caso, en el que el líder de proyecto no está familiarizado con tecnologías nuevas y por lo tanto deciden hacer el proyecto "a la antigüita" lo cual resta mucha productividad.
No existe la Sun GPL, la GPL es GPL y ya, lo que usaste seguramente es la Sun Public License (sin General). La SPL es muy similar a la LGPL. En resumen, si uso tu librería en un programa mio y lo distribuyo, debo distribuir también el código fuente de tu librería, con cualquier modificación que yo le haya hecho (las modificaciones que le haga a tu código quedan bajo SPL también). Pero el resto de mi programa no se vuelve SPL, no estoy obligado a dar código para eso. Esto sólo afecta en la distribución; yo puedo hacer un programa GPL y usarlo en mi casa y nunca tengo que mostrarle el código a nadie, a menos que se lo pase a alguien entonces sí tendria que darle el código si me lo pide. O puedo tomar un programa GPL y modificarlo y no tengo que darle las modificaciones a nadie si nada mas lo uso yo.
Es importante saber qué licencia le pones a tu software porque puedes estar limitando su uso o quitándote algunos privilegios... por ejemplo eso que dices de "usa el código como quieras y cuando quieras" es la licencia BSD, que te permite usar el software como parte de una aplicación cerrada sin tener que compartir las modificaciones y ni siquiera avisar que lo tienes (por eso Microsoft se agarró el código de TCP/IP de BSD pero al mismo tiempo odian la GPL, que los habría obligado a liberar todas las modificaciones que le hicieron, wrappers, etc).
Por último, es importante que sepas que si tu librería la hiciste como parte de tu chamba, seguramente ese código no es tuyo, sino de la consultora donde trabajas, o incluso de tu cliente, así que necesitas permiso del dueño del código para poderlo liberar.
Given the choice of dancing pigs and security, users will choose dancing pigs, every single time.
Steve Riley
ahh ok ya veo cual es la
ahh ok ya veo cual es la diferencia de las licencias, hahah pa la proxima leere con mas detalle, bueno la libreria la desarrolle por mi cuenta sin ayuda del trabajo, de hecho he estado proponiendo un centro de investigacion o algo parecido donde exista un equipo de evaluacion de tecnologia y a la vez capacitacion, y sean ellos los que digan que tecnologia es bueno para que casos de desarrollo, digamos que no me apoyan en nada que quisiera hacer o proponer, asi que el codigo es mio, lo hice un fin de semana que estaba aburrido en mi casa.
Aqui usan mucho el Savvion BPM solo para vernder mas o jBPM(me gusta mas, pq da mas flexibilidad para codear, el Savvion es para Non Coders), ya que hemos tenido muchos problemas con la herramienta, osea yo me declaro enemigo # 1 del Savvion BPM ya que limita mucho la flexibilidad y la programacion(como ya dije es para non coders), y se puede hacer lo mismo con un desarrollo tradicional (de hecho 4 desarrollos en Savvion BPM y jBPM fueron una catastrofe pq lo partieron por etapas y cuando se llegaba a la ultima etapa, nos dabamos cuenta que se queria un desarrollo tradicional) usando librarias muy completas como serian Hibernate, Sprint, Struts o JSF, pero como dicen en mi pais "con la plata baila el mono" prefieren vender mas (que no esta mal siempre y cuando sepan en lo que se meten) sin pensar en tiempos de entrega y esfuerzo del personal.
Sun Certified Java Developer
Sun Certified Java Programer
Brainbench Certified JavaServer Pages