Insertar muchos valores (con FK) en la base de datos usando LiquiBase y Spring
Frecuentes
Visto 26,037 equipos
22
Estoy tratando de agregar muchos registros (actualmente ubicados en un archivo de Excel) en mi base de datos usando Liquibase (para saber cómo hacerlo para futuros cambios en la base de datos)
Mi idea era leer el archivo de Excel usando Java y luego completar los ChangeLogParameters de mi clase de inicialización de Spring de esta manera:
SpringLiquibase liqui = new SpringLiquibase();
liqui.setBeanName("liquibaseBean");
liqui.setDataSource(dataSource());
liqui.setChangeLog("classpath:changelog.xml");
HashMap<String, String> values = new HashMap<String, String>();
values.put("line1col1", ExcelValue1);
values.put("line1col2", ExcelValue2);
values.put("line1col3", ExcelValue3);
values.put("line2col1", ExcelValue4);
values.put("line2col2", ExcelValue5);
values.put("line2col3", ExcelValue6);
...
liqui.setChangeLogParameters(values);
El problema con este enfoque es que mi changelog.xml sería muy extraño (y no productivo)
<changeSet author="gcardoso" id="2012082707">
<insert tableName="t_user">
<column name="login" value="${ExcelValue1}"/>
<column name="name" value="${ExcelValue2}}"/>
<column name="password" value="${ExcelValue3}"/>
</insert>
<insert tableName="t_user">
<column name="login" value="${ExcelValue4}"/>
<column name="name" value="${ExcelValue5}}"/>
<column name="password" value="${ExcelValue6}"/>
</insert>
...
</changeSet>
¿Hay alguna manera de que pueda hacer algo como esto:
HashMap<String, ArrayList<String>> values = new HashMap<String, ArrayList<String>>();
values.put("col1", Column1);
values.put("col2", Column2);
values.put("col3", Column3);
liqui.setChangeLogParameters(values);
<changeSet author="gcardoso" id="2012082707">
<insert tableName="t_user">
<column name="login" value="${Column1}"/>
<column name="name" value="${Column2}}"/>
<column name="password" value="${Column3}"/>
</insert>
</changeSet>
o hay otra manera?
EDIT: Mi opción actual es convertir Excel en un archivo CSV e importar los datos usando
<changeSet author="gcardoso" id="InitialImport2" runOnChange="true">
<loadData tableName="T_ENTITY" file="com/exictos/dbUpdate/entity.csv">
<column header="SHORTNAME" name="SHORTNAME" />
<column header="DESCRIPTION" name="DESCRIPTION" />
</loadData>
<loadData tableName="T_CLIENT" file="com/exictos/dbUpdate/client.csv">
<column header="fdbhdf" name="ENTITYID" defaultValueComputed="(SELECT ID FROM T_ENTITY WHERE SHORTNAME = ENTITY_REFERENCE"/>
<column header="DESCRIPTION" name="DESCRIPTION" />
</loadData>
</changeSet>
con estos archivos CSV:
entidad.csv
SHORTNAME,DESCRIPTION
nome1,descricao1
nome2,descricao2
cliente.csv
DESCRIPTION,ENTITY_REFERENCE
descricaoCliente1,nome1
descricaoCliente2,nome2
Pero me sale este error:
liquibase.exception.DatabaseException: Error executing SQL INSERT INTO `T_CLIENT` (`DESCRIPTION`, `ENTITY_REFERENCE`) VALUES ('descricaoCliente1', 'nome1'): Unknown column 'ENTITY_REFERENCE' in 'field list'
Si cambio el encabezado de mi client.csv a DESCRIPTION,ENTITYID, obtengo este error:
liquibase.exception.DatabaseException: Error executing SQL INSERT INTO `T_CLIENT` (`DESCRIPTION`, `ENTITYID`) VALUES ('descricaoCliente1', 'nome1'): Incorrect integer value: 'nome1' for column 'entityid' at row 1
En cualquiera de estos casos, parece que defaultValueComputed no funciona de la misma manera que valueComputed en el siguiente ejemplo
<changeSet author="gcardoso" id="InitialImport1">
<insert tableName="T_ENTITY">
<column name="SHORTNAME">nome1</column>
<column name="DESCRIPTION">descricao1</column>
</insert>
<insert tableName="T_CLIENT">
<column name="ENTITYID" valueComputed="(SELECT ID FROM T_ENTITY WHERE SHORTNAME = 'nome1')"/>
<column name="DESCRIPTION">descricaoCliente</column>
</insert>
</changeSet>
¿Es este el comportamiento esperado? ¿Error de LiquiBase? ¿O simplemente yo estoy haciendo algo mal (lo más probable)?
¿O hay alguna otra forma de importar una gran cantidad de datos? Pero siempre usando LiquiBase y/o Spring.
EDIT2: Mi problema es que no puedo insertar los datos en la segunda tabla con la clave externa correcta
2 Respuestas
11
Diría que Liquibase no es la herramienta ideal para lo que quieres lograr. Liquibase es adecuado para administrar la estructura de la base de datos, no los datos de la base de datos.
Si aún desea utilizar Liquibase para administrar los datos, tiene un par de opciones (consulte aquí) -
Registre sus declaraciones de inserción como SQL y consúltelas desde changelog.xml de esta manera:
<sqlFile path="/path/to/file.sql"/>
Utilizar Clase de refactorización personalizada al que se refiere desde changelog.xml de esta manera:
<customChange class="com.example.YourJavaClass" csvFile="/path/to/file.csv"/>
YourJavaClass leería los registros del archivo CSV y los aplicaría a la base de datos, implementando este método:
void execute(Database database) throws CustomChangeException;
Tenga en cuenta que una vez que haya cargado estos datos a través de Liquibase, no debe modificar los datos en el archivo, ya que esos cambios no se volverán a aplicar. Si desea realizar cambios en él, deberá hacerlo en conjuntos de cambios posteriores. Entonces, después de un tiempo, puede terminar con muchos archivos CSV/conjuntos de cambios de liquibase diferentes, todos operando con los mismos/datos similares (esto depende de cómo vaya a usar estos datos, ¿alguna vez cambiará una vez insertado?).
Recomendaría mirar usando unidad DB para la gestión de sus datos de referencia. Es una herramienta utilizada principalmente en pruebas unitarias, pero es muy madura, diría que adecuada para su uso en producción. Puede almacenar información en CSV o XML. Sugeriría usar un Spring 'InitializingBean' para cargar el conjunto de datos desde el classpath y realizar una operación de 'actualización' de DBUnit, que, desde el documentos:
Esta operación actualiza literalmente el contenido del conjunto de datos en la base de datos. Esto significa que los datos de las filas existentes se actualizan y se insertan las filas que no existen. Las filas que existen en la base de datos pero no en el conjunto de datos no se ven afectadas.
De esta manera, puede mantener sus datos de referencia en un solo lugar y agregarlos con el tiempo para que solo haya una fuente de información y no se divida en varios conjuntos de cambios de Liquibase. Mantener sus conjuntos de datos de DBUnit en el control de versiones proporcionaría capacidad de rastreo y, como beneficio adicional, los conjuntos de datos de DBUnit son portátiles entre bases de datos y pueden administrar cosas como el orden de inserción para evitar violaciones de claves externas para usted.
contestado el 23 de mayo de 17 a las 13:05
Estoy de acuerdo en que DBUnit es un buen enfoque para un enfoque Java+UnitTest para cargar datos. Desafortunadamente, no escalará si los requisitos son para cantidades "masivas" de datos. - Brad
Utilizo DBUnit en las pruebas de integración para cargar un conjunto de datos de 4 MB que abarca > 100 tablas, y tarda < 1 s contra HSQLDB y 10 s contra MySQL. Dudo que Liquibase pueda cargar los datos mucho más rápido, porque al final del día también usa JDBC. El OP está buscando una solución "siempre usando LiquiBase y/o Spring", aunque estoy de acuerdo en que para> 1 GB, las herramientas nativas serían mejores. - barry pitman
Mi problema es que no puedo insertar las claves foráneas en mi tabla (y por lo que descubrí que DBUnit tiene el mismo problema), y la solución para ambos casos se ve así: inserte un registro en la tabla con un FK falso, luego actualice su FK al correcto. PD: Mi archivo de importación inicial es de alrededor de 200 MB, y mis futuras actualizaciones serán de alrededor de 50 MB - gonzalo cardoso
No debería tener problemas para "insertar" fk con DBUnit, ya que lo admite siempre que su base de datos lo permita. He tenido problemas con fk usando CLEAN_INSERT. Vea mi publicación para una posible solución. Entonces, el problema era "eliminar", no "insertar". - Brad
Mi problema es que necesito insertar en una "confirmación única" dos tablas diferentes donde la segunda necesita agregar una clave externa a la primera: gonzalo cardoso
2
Depende de su base de datos de destino. Si estás usando Sybase or MSSQL servidor entonces puede usar el herramienta BCP que viene junto con su cliente + controlador instalado. Es la forma más rápida de mover grandes cantidades de datos dentro y fuera de estas bases de datos.
Googleando también encontré estos enlaces...
Oracle tiene el SQL*CARGADOR del IRS
MySQL tiene la CARGAR DATOS INFILE comando
Esperaría que cada proveedor de bases de datos proporcione una herramienta de alguna descripción para la carga masiva de datos.
Respondido 30 ago 12, 17:08
¿Dónde interviene LiquiBase o Spring en esa respuesta? - gonzalo cardoso
Estaba respondiendo a su declaración al final de su pregunta ... "¿O hay alguna otra forma de importar una gran cantidad de datos?". Supuse que no estabas recibiendo ninguna alegría con ninguna respuesta y que esto podría ser útil. - Brad
Está bien. Mi otra forma de preguntar es sobre LiquiBase y/o Spring;) Acabo de editar la publicación original para aclararla: gonzalo cardoso
Se agregaron nuevas alternativas y resultados de prueba: Gonçalo Cardoso
¿Podría aclarar para qué se van a utilizar los datos iniciales? ¿Son datos de referencia que normalmente no van a cambiar, pero que son necesarios para que se inicie la aplicación? ¿Se insertará solo una vez? - Barry Pitman
Son datos iniciales para que la aplicación funcione, pero también necesito una solución que funcione para cualquier actualización de la base de datos, porque habrá muchas actualizaciones que requerirán que se agregue mucha información. - Gonçalo Cardoso