Cuando uso el lote de primavera, tengo el requisito de escribir los datos en dos tablas diferentes. Parece que puedo usar un CompositeItemWriter
con tantos ItemWriters
como sea posible.
Sin embargo, necesito insertar un registro en la primera tabla y recuperar la clave principal generada automáticamente y usarla al insertarla en la segunda tabla. ¿Alguna idea de cómo podemos recuperar la clave generada automáticamente en este escenario?
La solución de Wis funciona, pero desde el punto de vista de su rendimiento (que normalmente es una de las principales preocupaciones cuando se habla de procesamiento por lotes), no se recomienda.
Pregunta: ¿cómo seleccionaría las filas insertadas para leer las claves generadas? ¿Tiene otro campo único, o al menos una combinación de él que le permita identificar los elementos escritos? ¿Puede leer al menos todos los elementos de la lista de 'elementos' como una sola llamada a la base de datos o tiene que leer cada entrada con su propia selección?
Al menos, tendrá una llamada de lectura adicional a la base de datos. pero también podría ser una llamada adicional por artículo.
La solución mucho mejor sería poder crear la clave dentro de su lote.
El rendimiento puede no ser un problema si solo hablamos de un par de miles de inserciones. Sin embargo, si hablamos de millones, importará.
De todos modos, si realmente tuviera que volver a leer desde la base de datos, usaría los siguientes dos enfoques.
Primero, si no tengo que escribir ambas tablas en la misma transacción
Paso 1:
Paso 2:
Segundo, si tengo que escribir ambas tablas en la misma transacción
public class MyWriter implements ItemWriter<MyObject> { private JdbcItemWriter<MyObject> writer1; private JdbcItemWriter<MyObject> writer2; @PostConstruct public void afterPropertiesSet() { writer1 = new JdbcItemWriter<MyObject>(); writer1.set... writer1.afterPropertiesSet(); writer2 = ... same thing } public void write(List<MyObject> items) { writer1.write(items); List<MyObject> reReadItems = new JdbcTemplate(datasource) // you could use a row that which has a unique chunk id // or you could construct a query with an appropriate // in-Clause... however, the size of possible in-clauses // is limited, for instance oracle has 1000 .query(a query that selects only the entries you inserted above, (resultset, row) -> // RowMapper { MyObject obj = new MyObject(); obj.setXy(resultset.get...); ... return obj; }); writer2.write(reReadItems); } }
Esto al menos limitaría sus llamadas a la base de datos a una llamada adicional por fragmento.
Los escritores siempre se ejecutan en orden. En consecuencia, puede escribir en la primera tabla Y leer la clave generada en el primer escritor y escribir en la segunda tabla en el segundo escritor. Si actualizó el objeto en el primer escritor, obtendrá la actualización en el segundo escritor.
El primer escritor puede parecerse a:
public class MyWriter implements ItemWriter<MyObject> { @Override public void write(List<? extends MyObject> items) throws Exception { // assuming there is a DB connection for(MyObject item: items) { // create and execute query to insert data in table 1 // get generated key and assign it into object int key = rs.getInt(...); item.setGeneratedKey(key); } } }
El segundo escritor puede usar el campo de clave generatedKey
para construir la consulta para la tabla 2. A medida que inyectó el valor en el primer escritor, estará disponible para el segundo escritor.
Soy nuevo para el arranque por lotes de Spring. Pero obtuve mucha experiencia en lotes de mainframe. Lo haría de esta manera suponiendo que tiene un archivo de entrada: 1- hacer un trabajo que inserte solo su entidad A con claves generadas 2- hacer un trabajo (o pasos) que amplíe su entrada con clave generada leyendo db. Salida = entrada + clave generada 3- haz un trabajo que indague tu entidad B Así que no intentes hacerlo en un solo paso...