procesamiento - ¿Por qué prefiere Java 8 Stream API en lugar de consultas directas de hibernate/sql cuando trabaja con la base de datos?
stream java 8 ejemplo (6)
Recientemente, veo muchos códigos en algunos proyectos que usan flujos para filtrar objetos, como:
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.limit(15)
.collect(toList()));
¿Hay alguna ventaja de usar eso en lugar de dirigir la consulta HQL / SQL a la base de datos y devolver los resultados filtrados?
¿No es el segundo enfoque mucho más rápido?
A menos que sea medido y comprobado para un escenario específico , puede ser bueno o igual de malo. La razón por la que normalmente desea llevar este tipo de consultas a la base de datos es porque (entre otras cosas):
DB puede manejar datos mucho más grandes que su proceso Java
Las consultas en una base de datos pueden ser indexadas (haciéndolas mucho más rápidas)
Por otro lado, si sus datos son pequeños, usar un Stream
la forma en que lo hizo es efectivo. Escribir una canalización de Stream es muy legible (una vez que hablas Streams lo suficientemente bien).
A primera vista: se puede hacer que las secuencias se ejecuten en paralelo; simplemente cambiando el código para usar parallelStream()
. (descargo de responsabilidad: por supuesto, depende del contexto específico si solo cambiar el tipo de flujo dará como resultado resultados correctos; pero sí, puede ser así de fácil).
Luego: transmite "invitar" a usar expresiones lambda. Y aquellos a su vez llevan al uso de instrucciones de invoke_dynamic byte invoke_dynamic ; a veces obteniendo ventajas de rendimiento en comparación con el tipo de escritura "de la vieja escuela". (y para aclarar el malentendido: ¡invoke_dynamic es una propiedad de las lambdas, no de las corrientes!)
Estas serían razones para preferir soluciones de "flujo" en la actualidad (desde un punto de vista general).
Más allá de eso: realmente depende ... veamos su ejemplo de entrada. Esto parece tratarse con los POJO de Java ordinarios, que ya residen en la memoria, dentro de algún tipo de colección. ¡El procesamiento de tales objetos en la memoria directamente sería definitivamente más rápido que ir a alguna base de datos fuera de proceso para realizar el trabajo allí!
Pero, por supuesto: cuando las llamadas anteriores, como book.getAuthor()
, estarían realizando una "inmersión profunda" y en realidad hablarían con una base de datos subyacente; entonces es probable que "hacer todo en una sola consulta" le brinde un mejor rendimiento.
Bueno, su pregunta debería ser idealmente: ¿es mejor hacer operaciones de reducción / filtrado en la base de datos o recuperar todos los registros y hacerlo en Java utilizando Streams?
La respuesta no es sencilla y las estadísticas que dan una respuesta "concreta" no se generalizarán en todos los casos.
Las operaciones de las que está hablando se realizan mejor en la propia base de datos, ya que es para lo que están diseñadas las bases de datos, un manejo muy rápido de los datos . Por supuesto, por lo general, en el caso de las bases de datos relacionales, se utilizarán algunos "registros y bloqueos" para garantizar que las transacciones independientes no terminen haciendo que los datos sean inconsistentes, pero incluso con eso, los DB hacen un buen trabajo de filtrado. Datos, especialmente grandes conjuntos de datos.
Un caso en el que preferiría filtrar los datos en el código Java en lugar de en la base de datos sería si necesita filtrar diferentes características de los mismos datos. Por ejemplo, en este momento solo obtiene el apellido del autor. Si desea obtener todos los libros escritos por el autor, las edades de los autores, los hijos del autor, el lugar de nacimiento, etc. Entonces tiene sentido obtener solo una copia de "solo lectura" de la base de datos y utilizar flujos paralelos para obtener información diferente. del mismo conjunto de datos.
Hibernate y otros ORM suelen ser mucho más útiles para escribir entidades en lugar de leer, porque permiten a los desarrolladores descargar pedidos de escrituras específicas al marco que casi nunca "se equivocarán".
Ahora, para leer e informar, por otra parte (y considerando que estamos hablando de DB aquí) es probable que una consulta SQL sea mejor porque no habrá ningún marco intermedio, y podrá ajustar el rendimiento de la consulta en términos de la base de datos que invocará esta consulta en lugar de en términos del marco de su elección, lo que le da más flexibilidad a la forma en que se puede hacer ese ajuste.
Lo primero es darse cuenta, que no se puede decir con solo este código, qué declaración se emite contra la base de datos. Podría muy bien, que se recopile todo el filtrado, la limitación y el mapeo, y luego de invocar la collect
toda esa información se usa para construir una declaración de SQL coincidente (o cualquier lenguaje de consulta que se use) y enviar a la base de datos.
Teniendo esto en cuenta, hay muchas razones por las que se usan APIs parecidas a los flujos.
Es cadera Las secuencias y las lambdas son todavía bastante nuevas para la mayoría de los desarrolladores de Java, por lo que se sienten bien cuando lo usan.
Si se usa algo como el primer párrafo, en realidad crea un DSL agradable para construir sus declaraciones de consulta. Scalas Slick y .Net LINQ donde conocí los primeros ejemplos, aunque supongo que alguien construye algo así en LISP mucho antes de que yo naciera.
Los flujos pueden ser flujos reactivos y encapsular una API no bloqueante. Si bien estas API son realmente buenas porque no te obligan a bloquear recursos como subprocesos mientras esperas los resultados. El uso de ellos requiere toneladas de devoluciones de llamada o el uso de una API basada en un flujo mucho mejor para procesar los resultados.
Son más agradables para leer el código imperativo. Tal vez el procesamiento realizado en el flujo no pueda realizarse [fácilmente por el autor] con SQL. Así que las alternativas no son SQL vs Java (o cualquier lenguaje que esté usando), sino Java imperativo o Java "funcional". El último a menudo se lee mejor.
Así que hay buenas razones para usar una API de este tipo.
Con todo lo dicho: es, en casi todos los casos, una mala idea hacer cualquier clasificación / filtrado y similares en su aplicación, cuando puede descargarlo a la base de datos. La única excepción que se me ocurre actualmente es cuando puede omitir todo el viaje de ida y vuelta a la base de datos, porque ya tiene el resultado localmente (por ejemplo, en un caché).
Si los datos provienen originalmente de una base de datos, es mejor hacer el filtrado en la base de datos en lugar de buscar todo y filtrar localmente.
Primero, los sistemas de administración de bases de datos son buenos para el filtrado, es parte de su trabajo principal y, por lo tanto, están optimizados para ello. El filtrado también se puede acelerar mediante índices.
En segundo lugar, recuperar y transmitir muchos registros y desarmar los datos en objetos solo para eliminar muchos de ellos cuando se realiza un filtrado local es un desperdicio de ancho de banda y recursos informáticos.