Announcement Announcement Module
Collapse
No announcement yet.
How To: Write a List of Items with a ItemWriter Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How To: Write a List of Items with a ItemWriter

    Hello Guys

    I have the follow code, trying to make a solution for this case
    How To: Read Once and Write 3 times in parallel

    First, the step is

    Code:
    <batch:step id="clienteCuentaCabeceraStep" >
    	<batch:tasklet transaction-manager="transactionManager">
    		<batch:chunk reader="clienteJdbcPagingItemReader" 
    				   processor="clienteCuentaCabeceraListItemProcessor"
    				   writer="clienteCuentaCabeceraJdbcBatchWriter"
    				   commit-interval="50"
    					/>								
    	</batch:tasklet>
    </batch:step>
    The processor is

    Code:
    public class ClienteCuentaCabeceraListItemProcessor implements ItemProcessor<Cliente,List<ClienteCuentaCabecera>>{
    	
    	....	
    	
    	@Override
    	public List<ClienteCuentaCabecera> process(Cliente cliente) throws Exception {
    		
    		List<ClienteCuentaCabecera> clientesCuentaCabecera = new ArrayList<ClienteCuentaCabecera>();
    		
    		for(int i=0;i<3;i++){
    		     ....
    		}
    				
    		return clientesCuentaCabecera;
    	}
    }
    Each List contains three items

    The writer is

    Code:
    <bean id="clienteCuentaCabeceraJdbcBatchWriter"
    	      class="org.springframework.batch.item.database.JdbcBatchItemWriter">
    	      <property name="dataSource" ref="dataSource"/>
    		  <property name="sql" 
    		            value="INSERT INTO clientecuentacabecera(idClienteCuentaCabecera, fechaCreacionClienteCuentaCabecera, totalDisponibleClienteCuentaCabecera, estadoClienteCuentaCabecera, idCliente, idMoneda) 
    		                   VALUES(?,?,?,?,?,?)" />
    		  <property name="itemPreparedStatementSetter" >
    		  	<bean class="com.manuel.jordan.batch.writer.ClienteCuentaCabeceraItemPreparedStatementSetter"/>
    		  </property>
    	</bean>
    Where ClienteCuentaCabeceraItemPreparedStatementSetter is

    Code:
    public class ClienteCuentaCabeceraItemPreparedStatementSetter implements ItemPreparedStatementSetter<ClienteCuentaCabecera>{
    
    	@Override
    	public void setValues(ClienteCuentaCabecera clienteCuentaCabecera, PreparedStatement ps) throws SQLException {
    
    		ps.setString(1,clienteCuentaCabecera.getIdClienteCuentaCabecera() );
                    ...
    		
    		
    	}
    }
    When I execute the code I get

    Code:
    2012-10-29 13:42:46,421 DEBUG [org.springframework.batch.core.repository.dao.JdbcStepExecutionDao] - 
    <Truncating long message before update of StepExecution, original message is: java.lang.ClassCastException: 
    java.util.ArrayList cannot be cast to com.manuel.jordan.domain.ClienteCuentaCabecera
    	at 
    com.manuel.jordan.batch.writer.ClienteCuentaCabeceraItemPreparedStatementSetter.setValues(ClienteCuentaCabeceraItemPreparedStatementSetter.java:1)
    Well even when the JdbcBatchItemWriter can write a List of items, the SQL statement arise the exception because it expect a list of Items, not a List of List of items.

    Even when like playing I did the follow variation shown below

    Code:
    public class ClienteCuentaCabeceraListItemPreparedStatementSetter implements ItemPreparedStatementSetter<List<ClienteCuentaCabecera>>{
    
    	@Override
    	public void setValues(List<ClienteCuentaCabecera> clientesCuentaCabecera, PreparedStatement ps) throws SQLException {
    				
    		for(ClienteCuentaCabecera clienteCuentaCabecera : clientesCuentaCabecera){
    			
    		
    			ps.setString(1,clienteCuentaCabecera.getIdClienteCuentaCabecera() );
     		        ...
    		}
    	}
    }
    Always write the last item of each collection, the third

    Has sense because it just iterates, and the first and second are overrided by the third and this last value assignation is really the only what is write it in the DB.

    How I could handle this?

    I am thinking in use a ItemWriteAdapter to call a customized service where the repository work with the jdbcTemplate with the method batchUpdate method.

    Exists other way to handle this situation with Spring Batch itself?

    Thanks in advanced

  • #2
    Be careful here. Returning a list from the process method of the ItemProcessor means that you are going to get a List<List< ClienteCuentaCabecera>> in the ItemWriter.write method. This means that each call to the ClienteCuentaCabeceraListItemPreparedStatementSett er will expect you to map List<ClienteCuentaCabecera> to a single insert.

    Comment


    • #3
      Hello Michael

      Thanks for the reply.

      Returning a list from the process method of the ItemProcessor means that you are going to get a List<List< ClienteCuentaCabecera>> in the ItemWriter.write method.
      Yes, each List<ClienteCuentaCabecera> element of the List<List< ClienteCuentaCabecera>> master list has three items.

      This means that each call to the ClienteCuentaCabeceraListItemPreparedStatementSett er will expect you to map List<ClienteCuentaCabecera> to a single insert.
      Yes and ...

      I did the follow for the writer

      Code:
      <bean id="clienteCuentaCabeceraItemWriterAdapter"
      	       class="org.springframework.batch.item.adapter.ItemWriterAdapter">
      	       <property name="targetObject" ref="clienteCuentaCabeceraBoImpl"/>
      	       <property name="targetMethod" value="insertarClientesCuentaCabecera" />
      	 </bean>
      And finally in my DAO method has

      Code:
      @Override
       public void insertarClientesCuentaCabecera(List<ClienteCuentaCabecera> clientesCuentaCabecera){
      	
      		 String sql = "INSERT INTO clientecuentacabecera(" +
      		 			  "idClienteCuentaCabecera, " +
      		 			  "fechaCreacionClienteCuentaCabecera, " +
      		 			  "totalDisponibleClienteCuentaCabecera, " +
      		 			  "estadoClienteCuentaCabecera, " +
      		 			  "idCliente, " +
      		 			  "idMoneda" +
      		 			  ")VALUES(?,?,?,?,?,?)" ;
      		 		 		 
      		 List<Object[]> batch = new ArrayList<Object[]>(); 
      		 				 
      		 for (ClienteCuentaCabecera clienteCuentaCabecera : clientesCuentaCabecera) {
      	            Object[] values = new Object[] {
      	            		clienteCuentaCabecera.getIdClienteCuentaCabecera(),
      	            		clienteCuentaCabecera.getFechaCreacionClienteCuentaCabecera().toDate(),
      	            		clienteCuentaCabecera.getTotalDisponibleClienteCuentaCabecera(),
      	            		clienteCuentaCabecera.getEstadoClienteCuentaCabecera(),
      	            		clienteCuentaCabecera.getCliente().getIdCliente(),
      	            		clienteCuentaCabecera.getMoneda().getIdMoneda()
      	            };
      	            batch.add(values);
      	    }
      		 
      		 jdbcTemplate.batchUpdate(sql, batch);
      				 
       }
      And work. For my it has sense, but look weird for me that Spring Batch be working with jdbcTemplate.batchUpdate

      Pls let me know If you have a better suggestion.

      Remember that this solution is a way to work around for this thread
      How To: Read Once and Write 3 times in parallel

      Thank you

      Comment


      • #4
        I stand by my comments in the other thread. This will cause your counts to be off at a minimum.

        Comment

        Working...
        X