DBIx :: Class es una interfaz Perl popular para cualquier base de datos a la que pueda conectarse a través de DBI . Hay buena documentación para sus detalles técnicos, pero poca información sobre su uso adecuado (incluidas las situaciones en las que probablemente no la desee).
En muchas situaciones, las personas lo buscan reflexivamente porque piensan que deberían usarlo para todo lo que involucra una base de datos. Sin embargo, con mayor frecuencia lo vi mal utilizado hasta el punto en que se convierte en un punto de dolor. Mi pregunta durante las revisiones de código y arquitectura es siempre "¿Qué beneficio le está dando Foo?" Muy a menudo, los desarrolladores que veo en estas situaciones no pueden formar una respuesta coherente a eso. Pero entonces, a menudo tampoco entienden el SQL simple.
Durante los últimos meses he estado preguntando a la gente "¿Por qué usas DBIx :: Class?" y solo recibí una buena respuesta (y esa persona también pudo responder el seguimiento "¿Cuándo no la usarías?"). Peter Rabbitson, el desarrollador principal, se acercó a una respuesta en su entrevista en FLOSS Weekly , pero está un poco enterrado en el medio de la entrevista.
Entonces, ¿cómo puedo decidir si usar DBIx :: Class es apropiado para un proyecto?
Respuestas:
Antes de responder a la pregunta, creo que algunos antecedentes están en orden.
El núcleo del problema
Después de años de entrevistar y contratar desarrolladores, aprendí dos cosas:
La gran mayoría de los desarrolladores tienen muy poca experiencia en el diseño de bases de datos.
He notado una correlación floja entre los que no entienden las bases de datos y los que odian los ORM.
(Nota: y sí, sé que hay quienes entienden muy bien las bases de datos que odian los ORM)
Cuando la gente no entiende por qué claves externas son importantes, ¿por qué no incrusta el nombre del fabricante en la
item
mesa, o por quécustomer.address1
,customer.address2
ycustomer.address3
los campos no son una buena idea, la adición de un ORM para que sea más fácil para ellos errores de base de datos de escritura No va a ayudar nada.En cambio, con una base de datos diseñada adecuadamente y un caso de uso OLTP, los ORM son dorados. La mayor parte del trabajo duro desaparece y con herramientas como DBIx :: Class :: Schema :: Loader , puedo pasar de un buen esquema de base de datos a un código Perl en cuestión de minutos. Citaría la Regla de Pareto y diría que el 80% de mis problemas se han resuelto con el 20% del trabajo, pero en realidad, encuentro los beneficios aún mayores que eso.
Abusando de la solución
Otra razón por la que algunas personas odian los ORM es porque dejarán escapar la abstracción. Consideremos el caso común de las aplicaciones web MVC. Aquí hay algo que comúnmente vemos (pseudocódigo):
La gente escribe rutas de control así y se da palmaditas en la espalda, pensando que es un código bueno y limpio. Estarían horrorizados al codificar el SQL en sus controladores, pero han hecho poco más que exponer una sintaxis SQL diferente. Su código ORM debe insertarse en un modelo y luego pueden hacer esto:
¿Sabes lo que pasó ahora? Ha encapsulado correctamente su modelo, no ha expuesto el ORM, y más tarde, cuando descubra que puede obtener esos datos de un caché en lugar de la base de datos, no necesita cambiar el código de su controlador (y es más fácil escribir pruebas para ello y reutilizar la lógica).
En realidad, lo que sucede es que las personas filtran su código ORM en todos sus controladores (y vistas) y cuando se topan con problemas de escalabilidad, comienzan a culpar al ORM en lugar de a su arquitectura. El ORM tiene una mala reputación (lo veo repetidamente para muchos clientes). En su lugar, oculte esa abstracción para que cuando haya alcanzado realmente los límites de ORM, pueda elegir las soluciones apropiadas para su problema en lugar de permitir que el código esté tan estrechamente unido al ORM que esté atado.
Informes y otras limitaciones
Como Rob Kinyon dejó en claro anteriormente, los informes tienden a ser una debilidad en los ORM. Este es un subconjunto de un problema mayor en el que SQL complicado o SQL que abarca varias tablas a veces no funciona bien con ORM. Por ejemplo, a veces el ORM fuerza un tipo de unión que no quiero y no puedo decir cómo solucionarlo. O tal vez quiero usar una pista de índice en MySQL, pero no es fácil . O, a veces, el SQL es tan complicado que sería mejor escribir el SQL en lugar de la abstracción proporcionada.
Esta es parte de la razón por la que comencé a escribir DBIx :: Class :: Report . Hasta ahora funciona bien y resuelve la mayoría de los problemas que las personas tienen aquí (siempre y cuando estén bien con una interfaz de solo lectura). Y aunque parezca una muleta, en realidad, siempre y cuando no esté filtrando su abstracción (como se explicó en la sección anterior), hace que trabajar con él sea
DBIx::Class
aún más fácil.Entonces, ¿cuándo elegiría DBIx :: Class?
Para mí, lo elegiría la mayoría de las veces que necesito una interfaz para una base de datos. Lo he estado usando por años. Sin embargo, es posible que no lo elija para un sistema OLAP, y los programadores más nuevos ciertamente tendrán dificultades con él. Además, a menudo encuentro que necesito meta-programación y, aunque
DBIx::Class
proporciona las herramientas, están muy poco documentadas.La clave para usar
DBIx::Class
correctamente es la misma que para la mayoría de los ORM:No pierdas la abstracción.
Escribe tus malditas pruebas.
Sepa cómo desplegarse a SQL, según sea necesario.
Aprenda a normalizar una base de datos.
DBIx::Class
, una vez que lo aprenda, se encargará de la mayor parte de su trabajo pesado y hará que sea muy fácil escribir aplicaciones rápidamente.fuente
#dbix-class
y#catalyst
): la clave para el bit de "no filtrar la abstracción" es que cada cosa con la que está trabajando en DBIC es Una subclase de algo que proporciona el comportamiento de cortador de galletas. Le recomendamos encarecidamente que agregue métodos a sus subclases y, a menos que haga un trabajo de preguntas y respuestas, solo los métodos que escribió deben formar parte de su interfaz pública.Para saber cuándo usar algo, es importante comprender cuál es el propósito de la cosa. Cuál es el objetivo del producto.
DBIx :: Class es un ORM - Mapper Relacional de Objetos. Un ORM toma las estructuras de datos de bases de datos relacionales basadas en conjuntos / relacionales y las asigna a un árbol de objetos. El mapeo tradicional es un objeto por fila, usando la estructura de la tabla como una descripción de clase. Las relaciones padre-hijo en la base de datos se tratan como relaciones de contenedor entre objetos.
Eso es lo esencial. Pero eso no le ayuda a decidir si debe usar un ORM. Los ORM son principalmente útiles cuando se cumple lo siguiente:
La mayor fortaleza que tiene un ORM es construir un buen SQL para recorrer un gráfico de árbol superpuesto sobre la estructura de datos relacionales. El SQL a menudo es complicado y complejo, pero ese es el precio de administrar la falta de coincidencia de impedancia.
Si bien los ORM son muy buenos para escribir SQL de extracción de filas, son muy pobres para escribir SQL intercalado. Este es el tipo de SQL en el que se basan los informes. Este tipo de intercalación se crea utilizando diferentes herramientas, no un ORM.
Hay muchos ORM en varios idiomas, varios en Perl. Otros mapeadores son Class :: DBI y Rose :: DB. DBIx :: Class a menudo se considera mejor que los demás en gran medida por la solidez de sus conjuntos de resultados. Este es un concepto donde la generación de SQL está separada de la ejecución de SQL.
Actualización : en respuesta a Ovid, DBIx :: Class (a través de SQL :: Abstract) ofrece la capacidad de especificar qué columnas devolver y qué sugerencias de índice utilizar.
Sin embargo, en general, si desea hacer esto, es mejor que no use un ORM para esa consulta específica. Recuerde: el propósito principal de un ORM es asignar filas en una tabla a objetos de una clase cuyos atributos son las columnas de la tabla. Si solo está completando algunos de los atributos, los usuarios potenciales de ese objeto no sabrán qué atributos se rellenan o no. Esto lleva a una horrible programación defensiva y / o un odio general hacia los ORM.
Casi siempre, el deseo de usar sugerencias de índice o limitar qué columnas se devuelven es una optimización para la velocidad y / o una consulta agregada.
Sí, un gran DBA puede hacer que las tablas con filas de 100MM + funcionen en Oracle o SQL * Server. Si está leyendo esto, no tiene un excelente DBA en el personal.
Dicho todo esto, un buen ORM hace más que solo crear gráficos de objetos: también proporciona una definición introspectiva de su base de datos. Puede usar esto para ayudar a crear consultas ad-hoc y consumirlas como lo haría con DBI, sin crear el gráfico de objetos.
fuente
Como uno de los principales desarrolladores de la plataforma de comercio electrónico Interchange6 (y la motosierra de esquema principal) tengo una experiencia bastante profunda con DBIC. Estas son algunas de las cosas que la hacen una plataforma tan genial:
El generador de consultas le permite escribir una vez para muchos motores de bases de datos (y múltiples versiones de cada uno). Actualmente admitimos Interchange6 con MySQL, PostgreSQL y SQLite y agregaremos soporte para más motores una vez que tengamos el tiempo y los recursos. Actualmente, solo hay dos rutas de código en todo el proyecto que tienen un código adicional para satisfacer las diferencias entre los motores y esto se debe simplemente a la falta de una función de base de datos específica (falta SQLite en algunos lugares) o debido a la idiotez de MySQL que cambió la forma en que su función MENOS maneja NULL entre dos versiones menores.
Las consultas predefinidas significan que puedo crear métodos simples a los que se puede llamar (con o sin argumentos) desde el código de la aplicación, por lo que mantengo mis consultas principalmente dentro de la definición del esquema en lugar de ensuciar el código de mi aplicación.
La generación de consultas componibles permite dividir las consultas en pequeñas consultas predefinidas y entendibles y luego encadenarlas para crear consultas complejas que serían difíciles de mantener a largo plazo en DBIC (incluso peor en SQL puro) si se construyeran en un solo paso.
Schema :: Loader nos ha permitido usar DBIC con aplicaciones heredadas que nos dan una nueva oportunidad de vida y un camino mucho más simple hacia el futuro.
Los complementos DBIC, DeploymentHandler y Migration se agregan inmensamente al conjunto de herramientas que simplifican mi vida.
Una de las grandes diferencias entre DBIC y la mayoría de las otras plataformas similares a ORM / ORM es que, aunque trata de guiarte en su forma de hacer las cosas, tampoco te impide hacer locuras que te gustan:
Puede usar funciones SQL y procedimientos almacenados que DBIC no conoce simplemente al proporcionar el nombre de la función como clave en la consulta (también puede ser divertido cuando intenta usar LEAST en MySQL ^^ pero eso no es culpa de DBIC) .
El SQL literal se puede usar cuando no hay una 'forma DBIC' para hacer algo y el resultado devuelto todavía está envuelto en clases agradables con accesores.
TL; DR Probablemente no me molestaría en usarlo para aplicaciones realmente simples con solo un par de tablas, pero cuando necesito administrar algo más complejo, especialmente donde la compatibilidad entre motores y la mantenibilidad a largo plazo son clave, entonces DBIC es Generalmente mi camino preferido.
fuente
(Antes de comenzar, debería decir que esto solo compara los envoltorios DBI, DBI y DBI basados en Mojo. No tengo experiencia con ningún otro ORM de Perl, por lo que no comentaré sobre ellos directamente).
DBIC hace muchas cosas muy bien. No lo uso mucho, pero conozco la teoría. Hace un trabajo bastante bueno en la generación de SQL y especialmente (como me han dicho) manejando uniones, etc. También puede hacer una captación previa de otros datos relacionados.
La principal ventaja que veo es la capacidad de usar DIRECTAMENTE los resultados como su clase de modelo. Esto también se conoce como "agregar métodos de conjunto de resultados" en el que puede obtener sus resultados y llamar a métodos sobre esos resultados. El ejemplo común es buscar un objeto de usuario de DBIC y luego llamar a un método para verificar si su contraseña es válida.
La implementación del esquema seguro puede ser difícil, pero siempre es difícil. DBIC tiene herramientas (algunas en módulos externos) que lo hacen más fácil, y probablemente más fácil que administrar sus propios esquemas a mano.
En el otro lado de la moneda, hay otras herramientas que atraen a otras sensibilidades, como los envoltorios DBI con sabor a mojo. Estos tienen el atractivo de ser delgados y aún así utilizables. La mayoría también ha seguido el ejemplo de Mojo :: Pg (el original) y ha agregado funciones útiles como la administración de esquemas en archivos planos y la integración de pubsub.
Estos módulos con sabor a Mojo surgieron de otro punto débil de DBIC, que es que (todavía) no es capaz de hacer consultas asincrónicas. Los autores me han asegurado que es técnicamente posible tal vez incluso rápidamente, pero hay problemas para diseñar una API que sea adecuada. (Es cierto que incluso me han pedido que ayude con esto, y aunque lo haría, simplemente no sé cómo mover la aguja en el tiempo que tengo que dedicarle).
TL; DR usa DBIC a menos que ames SQL o necesites asíncrono, en cuyo caso investiga los envoltorios DBI con sabor a Mojo.
fuente
Escribí mis pensamientos sobre esto en DBIC vs DBI hace tres años. Para resumir, enumeré dos razones principales:
En cuanto a los antipatrones, bueno, lo único en lo que puedo pensar es en el rendimiento. Si realmente desea exprimir cada ciclo de reloj de su CPU, entonces tal vez DBIC no sea la herramienta adecuada para el trabajo. Pero, ciertamente para las aplicaciones que escriben, esos casos son cada vez más raros. No recuerdo la última vez que escribí una nueva aplicación que habló con una base de datos y no usó DBIC. Por supuesto, ayuda si sabe un poco sobre cómo ajustar las consultas que genera DBIC.
fuente
La forma en que lo hago escala:
cree una clase que proporcione el constructor de socket DBI y los métodos de prueba.
deriva esa clase en tus clases de consulta SQL (una clase por tabla sql) y prueba el socket en el momento del constructor.
use variables de ámbito de clase para mantener el nombre de la tabla y los nombres de las columnas del índice primario.
Escriba todo el nombre de la tabla de interpolación de SQL y la columna de índice primario de esas variables en lugar de definirlas en el SQL estáticamente.
use macros de editor para permitirle hacer pares de métodos DBI básicos (preparar y ejecutar) mientras escribe SOLO la instrucción sql.
Si puede hacer eso, puede escribir código API limpio sobre el DBI todo el día con relativa facilidad.
Lo que encontrará es que muchas de sus consultas serán portables en varias tablas. En ese punto, puede cortar y pegar en una clase EXPORTADOR y espolvorearlos nuevamente donde sea necesario. Aquí es donde entra en juego la interpolación de clase del nombre de la tabla y los nombres de las columnas del índice primario.
He utilizado este enfoque para escalar a cientos de métodos DBI con un rendimiento relativamente bueno. No quisiera intentar mantener el código DBI de otra manera.
Cuándo usar el DBI: siempre.
Sin embargo, no creo que esa sea tu verdadera pregunta. Su verdadera pregunta fue: "Esto parece una gran PITA, ¿por favor dime que no tengo que hacer esto?"
Usted no Extiéndalo bien y la parte DBI se vuelve lo suficientemente redundante como para poder automatizarla principalmente.
fuente
No soy un experto en Perl, pero lo uso mucho. Hay muchas cosas que no sé o sé cómo hacer mejor; algunas cosas que todavía no soy capaz de entender, a pesar de la documentación.
Tiendo a comenzar con DBI porque pienso: "Oh, este es un proyecto simple, no necesito la hinchazón de un ORM y no quiero molestarme con la configuración y los módulos de esquema". Pero muy rápidamente, casi siempre, empiezo a maldecirme por esa decisión. Cuando quiero comenzar a ser creativo en mis consultas SQL (consultas dinámicas, no solo marcadores de posición de comparación) me esfuerzo por mantener la cordura usando DBI. SQL :: Abstract ayuda mucho, y normalmente eso es suficiente para mí. Pero mi próxima lucha mental es mantener tanto SQL dentro de mi código. Me distrae mucho tener líneas y líneas de SQL incrustado dentro de feos heredocs. Tal vez necesito usar un IDE de calidad.
Al final, la mayoría de las veces, me quedo con DBI directo. Pero sigo deseando que hubiera una mejor manera. DBIx :: Class tiene algunas características realmente buenas y lo he usado varias veces, pero parece demasiado exagerado para todos, excepto para los proyectos más grandes. Ni siquiera estoy seguro de qué me resulta más engorroso administrar: DBI con SQL disperso o DBIC con módulos de esquema dispersos.
(Oh, cosas como restricciones y disparadores son una gran ventaja para DBIC).
fuente