Framework Spring JAVA Comment faire des batchs performants quand on à beaucoup de données?

Besoin d’insérer des fichiers csv dans une base de données? les fichiers sont gros du coup vous allez vous heurter à des problèmes de lenteurs

Vous allez devoir :

  • Lire le fichier par morceaux
  • Insérer par lots dans la base (batch)
  • Faire un commit tous les n lignes
  • Faire des threads pour traiter en parallèle le même fichier et gérer le problème de concurrence d’accès pour ne pas insérer 2 fois la même ligne

Alors si il fallait tout développer from scratch vous voyez vite c’est du boulot!

Spring Frameworks propose toutes ces fonctionnalités et je dois dire pour l’avoir utilisé dans des projets sur de très gros volumes ca le fait! au moent ou j’écris ce post j’utilise :

  • spring-batch-core-4.0.1.release.jar
  • spring-batch-infrastructure-4.0.1-release.jar

vous aurez besoin de définir 3 fichiers configs

  • context.xml les informations de contexte type de base jdbc et repository classe
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

  <!-- stored job-metadata in database -->
  <bean id="jobRepository"
	class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<property name="transactionManager" ref="transactionManager" />
	<property name="databaseType" value="mysql" />
  </bean>

  <!-- stored job-metadata in memory -->
  <!-- 
  <bean id="jobRepository"
	class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
	<property name="transactionManager" ref="transactionManager" />
  </bean>
   -->
 
  <bean id="jobLauncher"
	class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
	<property name="jobRepository" ref="jobRepository" />
  </bean>

</beans>
  • database.xml les informations de connexions à la base
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/jdbc 
	http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">

  <!-- connect to database -->
  <bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
	<property name="url" value="jdbc:mysql://" />
	<property name="username" value="" />
	<property name="password" value="" />
  </bean>

  <bean id="transactionManager"
	class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
	
  <!-- create job-meta tables automatically -->
  <jdbc:initialize-database data-source="dataSource">
	<jdbc:script location="org/springframework/batch/core/schema-drop-mysql.sql" />
	<jdbc:script location="org/springframework/batch/core/schema-mysql.sql" />
  </jdbc:initialize-database>

</beans>

  • job-documents.xml les classes qui vont être appeler par spring pour parser les fichiers le reader, faire les transformations nécésssaires avant l’insertion en base (mapper) , et la classe qui insére en base le writer
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:batch="http://www.springframework.org/schema/batch" 
	xmlns:task="http://www.springframework.org/schema/task"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/batch
	http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

  <bean id="document" class="groovel.models.DocumentsToProcess" scope="prototype" />
    
    <!--parsed all input files and extract userid  -->
  <batch:job id="documentJob">
	<batch:step id="step1">
	  <batch:tasklet>
		<batch:chunk reader="itemReader" processor="itemProcessor"  writer="itemWriter" 
			commit-interval="10">
		</batch:chunk>
	  </batch:tasklet>
	</batch:step>
  </batch:job>
  
   <bean id="itemProcessor" class="groovel.processors.DocumentsProcessor">
			<property name="documentDao" ref="documentDao" />
	</bean>
	
	
	<bean id="documentDao" class="groovel.dao.DocumentsDao">
	 <property name="dataSource" ref="dataSource" />
	</bean>
	
	
	
	<bean id="itemWriter" class="groovel.writers.DocumentsWriter">
  	  	<property name="documentDao" ref="documentDao" />
	</bean>
	
  <bean id="itemReader" class="org.springframework.batch.item.file.ResourcesItemReader">

	<!-- Read a pdf file -->
	 <property name="resources" value="file:/inputs/*.PDF" />

  </bean>
  
  
  <!-- build json job for laravel queue -->
  
  <bean id="jobWriter" class="test.writers.JobjsonWriter">
  	  	<property name="documentDao" ref="documentDao" />
	</bean>
  
  
  <bean id="jobReader"
        class="org.springframework.batch.item.database.JdbcCursorItemReader">
        <property name="dataSource" ref="dataSource" />
       <property name="sql"
            value="SELECT id FROM documents where in_error!=1 and is_processed!=1" />
 
        <property name="rowMapper">
            <bean class="test.mapper.RowMapperJobs" />
        </property>
 
    </bean>
  
     <bean id="jobProcessor" class="test.processors.JobsProcessor"></bean>
	
	
  <batch:job id="buildJsonJob">
	<batch:step id="step2">
	  <batch:tasklet>
		<batch:chunk reader="jobReader" processor="jobProcessor"  writer="jobWriter" 
			commit-interval="2">
		</batch:chunk>
	  </batch:tasklet>
	</batch:step>
  </batch:job>
  </beans>

Vous pouvez configurer un workflow et enchainer la succession d’exécutions de différents batches! en définissant des steps et c’est la ou spring est puissant et ou cette fonctionnalité est évidemment un must have!

le lien du projet :

https://spring.io/projects/spring-batch