[JAVA] Éléments à prendre en compte lors de l'exécution d'un travail spécifié à l'aide de Spring Batch

Aperçu

J'ai suivi le tutoriel sur la page officielle pour construire le projet et j'ai exécuté Spring Batch depuis IntelliJ IDEA. En plus de pouvoir utiliser les fonctions d'une application Spring Boot normale, elle a des résultats d'exécution de travail, des fonctions de relance, etc., et j'ai senti que la qualité en tant que cadre de traitement par lots était élevée. Cependant, lorsque j'exécute la classe principale de l'application à partir d'IntelliJ, tous les travaux définis sont exécutés et je ne savais pas comment exécuter uniquement le travail spécifié, je vais donc l'examiner. Surtout.

Exigences de l'application

En tant qu'exigence pour les applications par lots, je voulais au moins atteindre ce qui suit.

Méthode de réalisation

Étant donné que l'application existante est un script de transaction et qu'elle est devenue compliquée, nous envisageons d'introduire Spring Batch dans un contexte. Étant donné que la manière d'exécuter une application existante est similaire à l'interface de [CommandLineJobRunner](implémentation avec #CommandLineJobRunner) décrite plus tard, j'ai envisagé de l'implémenter ici, mais cette méthode ne pouvait pas répondre à l'exigence. Cependant, j'ai pu répondre aux exigences en commençant par JobLauncherCommandLineRunner. La structure des répertoires est la suivante pour divers fichiers.

├── build.gradle
└── src
    └── main
        ├── java
        │   └── hello
        │       ├── Application.java
        │       ├── BatchConfiguration.java
        │       ├── JobCompletionNotificationListener.java
        │       ├── Person.java
        │       └── PersonItemProcessor.java
        └── resources
            ├── application-development.properties
            ├── application-production.properties
            ├── application.properties
            ├── log4jdbc.log4j2.properties
            ├── sample-data.csv
            └── schema-all.sql

build.gradle Les dépendances suivantes ont été ajoutées pour appliquer la connexion à MySQL.

    runtime("mysql:mysql-connector-java")
    compile "org.lazyluke:log4jdbc-remix:0.2.7"
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")

application.properties Lors de l'utilisation de la fonction Spring Boot, l'application- $ {profile} .properties associée au profil spécifié est chargée dans le framework séparément de l'application parent.properties, et les informations d'environnement sont chargées dans Objet Environnement. Il est défini sur (: //docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/Environment.html). Les informations sur l'environnement sont requises par Datasource [spring.datasource. *](Https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot- Features-connect-to-production-database) peuvent être inclus.

appliaction-production.properties


spring.datasource.username=production_user
spring.datasource.password=production_password
spring.datasource.url=jdbc:log4jdbc:mysql://kasakaid.production.jp:3306/kasakaidDB?useSSL=true
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy

appliaction-development.properties


spring.datasource.username=root
spring.datasource.password=mysql
spring.datasource.url=jdbc:log4jdbc:mysql://127.0.0.1:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy

Comme mentionné ci-dessus, si vous préparez application.properties pour chaque profil, vous pouvez basculer la base de données pour se connecter avec le numéro d'environnement ou l'argument de la ligne de commande.

Schéma de base de données Spring Batch

__ 2019/09/07 Addendum __

Spring Batch écrit les résultats de l'exécution du travail, par exemple, ce travail a réussi ou échoué, dans le schéma de base de données intégré à Spring Batch. Plusieurs DDL pour créer ce schéma sont fournis pour accueillir diverses bases de données telles que MySQL et PostgreSQL, mais ce sont les build.gradle du projet Spring Batch. Lors de la construction avec /spring-projects/spring-batch/blob/547533fab072289c062916e51c589de36ea3dfe2/spring-batch-core/build.gradle), ce fichier /547533fab072289c062916e51c589de36ea3dfe2/spring-batch-core/src/main/sql/schema.sql.vpp) semble être généré en tant que modèle. Le comportement de création d'un schéma peut être trouvé sur la page officielle.

If you use Spring Batch, it comes pre-packaged with SQL initialization scripts for most popular database platforms. Spring Boot can detect your database type and execute those scripts on startup. If you use an embedded database, this happens by default. You can also enable it for any database type, as shown in the following example:

spring.batch.initialize-schema=always

Comme vous pouvez le voir, vous pouvez le configurer pour qu'il s'exécute toujours au démarrage de Spring Batch. Cependant, dans un environnement où les applications s'exécutent en permanence, y compris les environnements commerciaux, il est préférable de ne pas toujours effectuer cette opération de création de schéma au démarrage de Spring Batch. En effet, le processus par lots réussit au démarrage de Spring Batch, mais lorsque cette opération de création de schéma est exécutée, le schéma a déjà été généré, de sorte qu'une erreur est toujours levée lors de la création du schéma. Il semble qu'il n'y ait aucun paramètre pour changer le comportement dans la version actuelle, en disant "Si le schéma est créé, ne faites rien". Étant donné que cette erreur provoque du bruit, il n'y a aucun moyen de le faire, alors créez à l'avance un schéma pour Spring Batch dans l'environnement correspondant. Le DDL lui-même peut être confirmé en regardant sous org.springframework.batch.core dans org.springframework.batch: spring-batch-core: .RELEASE avec IntelliJ etc. Parmi ceux-ci, sélectionnez le DDL de la base de données utilisée dans l'environnement approprié et exécutez-le dans l'environnement approprié.

Screen Shot 2019-09-07 at 15.18.40.png Screen Shot 2019-09-07 at 15.18.49.png

Après avoir créé le schéma, vous n'avez pas besoin de créer le schéma, en particulier lorsque Spring Batch est démarré, alors ne définissez aucun pour que le schéma ne soit pas créé dans un environnement commercial.

application-production.properties


spring.batch.initialize-schema=none

D'autre part, les bases de données volatiles en mémoire telles que H2 doivent généralement être utilisées pendant les tests, assurez-vous donc de créer un schéma pendant les tests. (Cependant, si vous n'utilisez pas le mécanisme Spring Batch et testez uniquement à partir de la classe Service, etc., vous n'avez pas besoin de créer un schéma en particulier)

application-test.properties


spring.batch.initialize-schema=always

log4jdbc.log4j2.properties Il s'agit du fichier de configuration requis pour utiliser log4jdbc. Non lié à Spring Batch, mais décrit pour répondre aux exigences.

log4jdbc.log4j2.properties


log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

Spécification des propriétés du système

Après avoir effectué la configuration ci-dessus, lors de l'exécution de l'application Spring Batch, sélectionnez deux Propriétés système. Spécifier.

No Propriétés du système La description
1 spring.profiles.active Profil à activer
2 spring.batch.job.names Le nom du travail à exécuter

Le premier spring.profiles.active est le profil à activer. Le deuxième [spring.batch.job.names](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-batch-applications.html Vous pouvez spécifier le nom du travail à exécuter avec).

Exécution et résultats

Exécutez le lot avec les propriétés système. Les propriétés système peuvent être spécifiées comme arguments d'exécution ou définies comme variables d'environnement. L'exemple ci-dessous exécute deux propriétés système en tant qu'arguments d'exécution.

java -Dspring.profiles.active=development -Dspring.batch.job.names=importUserJob -jar gs-batch-processing-0.1.0.jar

Vous pouvez accéder à MySQL en spécifiant votre profil de développement! Bien sûr, l'art ASCII de Spring Boot est également affiché.


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)

2019-01-20 23:16:58.028  INFO 86837 --- [           main] hello.Application                        : Starting Application on sakaidaikki-no-MacBook-Air.local with PID 86837 (/Users/kasakaid/dev/java/gs-batch-processing/complete/build/libs/gs-batch-processing-0.1.0.jar started by kasakaid in /Users/kasakaid/dev/java/gs-batch-processing/complete/build/libs)
2019-01-20 23:16:58.034  INFO 86837 --- [           main] hello.Application                        : The following profiles are active: development
2019-01-20 23:17:03.570  INFO 86837 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2019-01-20 23:24:25.447  INFO 87366 --- [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: MYSQL
2019-01-20 23:24:25.906  INFO 87366 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
2019-01-20 23:24:25.934  INFO 87366 --- [           main] jdbc.audit                               : 1. Connection.getMetaData() returned com.mysql.jdbc.JDBC4DatabaseMetaData@14cd1699
2019-01-20 23:24:25.935  INFO 87366 --- [           main] jdbc.audit                               : 1. Connection.clearWarnings() returned 
2019-01-20 23:24:25.942  INFO 87366 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from class path resource [org/springframework/batch/core/schema-mysql.sql]

À propos de la réalisation avec CommandLineJobRunner

En regardant la page officielle, si vous utilisez la classe CommandLineJobRunner, vous pouvez spécifier le travail avec les arguments suivants. Il y a.

No argument La description
1 jobPath The location of the XML file that will be used to create an ApplicationContext. This file should contain everything needed to run the complete Job
2 jobName The name of the job to be run.

<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date(date)=2007/05/05

Si le batch est réalisé par cette méthode, la classe avec la fonction principale à exécuter en premier doit être la classe CommandLineJobRunner. Par conséquent, modifiez la section de tâches buildJar de build.graldle pour changer la classe de démarrage de la classe hello.Application par défaut en CommandLineJobRunner.

bootJar {
    baseName = 'gs-batch-processing'
    version =  '0.1.0'
    manifest {
        attributes 'Start-Class': 'org.springframework.batch.core.launch.support.CommandLineJobRunner'
    }
}

Si vous exécutez la tâche bootJar dans cet état, un fichier jar sera généré sous build / libs. Décompressez le fichier jar et recherchez META dans spécifications du fichier jar -Si vous vérifiez INF / MANIFEST.MF, vous pouvez voir que les changements dans build.gradle sont reflétés. La classe principale (https://docs.oracle.com/javase/tutorial/deployment/jar/appman.html) spécifie toujours org.springframework.boot.loader.JarLauncher. Et la spécification Spring Boot Start-Class Est hello.Application par défaut, mais change en CommandLineJobRunner.

MANIFEST.MF


Manifest-Version: 1.0
Start-Class: org.springframework.batch.core.launch.support.CommandLineJobRunner
Main-Class: org.springframework.boot.loader.JarLauncher

Exécuter avec des arguments

Vous avez correctement défini la classe avec la fonction principale dans le fichier jar. Désormais, lorsque vous exécutez ce jar, la fonction principale CommandLineJobRunner sera lancée en premier. Exécutez avec le fichier jar généré spécifié dans l'argument de -jar.

java -Dspring.profiles.active=development -jar gs-batch-processing-0.1.0.jar hello.BatchConfiguration importUserJob

Un profil est toujours requis en tant que propriété système, mais le nom de travail spécifié dans spring.batch.job.names sera spécifié comme premier argument comme jobPath. Le deuxième argument spécifie le importUserJob défini dans @ Bean. Je pensais que cela se terminerait normalement, mais lorsque j'ai exécuté l'application, j'ai eu l'erreur suivante.

21:12:52.267 [main] ERROR org.springframework.batch.core.launch.support.CommandLineJobRunner - Job Terminated in error: Error creating bean with name 'writer' defined in hello.BatchConfiguration: Unsatisfied dependency expressed through method 'writer' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'writer' defined in hello.BatchConfiguration: Unsatisfied dependency expressed through method 'writer' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732)

Dans la définition de writer inclus dans l'exemple, une dataSource est spécifiée avec dataSource comme argument, mais je pense que l'argument est 0.

BatchConfiguration.java


    @Bean
    public JdbcBatchItemWriter<Person> writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder<Person>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)")
            .dataSource(dataSource)
            .build();
    }

Spécifiez @ SpringBootApplication

Je me suis demandé ce qui était arrivé à cela, mais il n'y a que deux annotations dans la classe BatchConfiguration:

Je ne pense pas qu'il y ait de définition pour Spring Boot, j'ai donc changé l'annotation de @ Configuration en [ @ SpringBootApplication](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot -using-springbootapplication-annotation.html). C'est parce que @ SpringBootApplication inclut également @ Configuration.

BatchConfiguration.java


@SpringBootApplication
@EnableBatchProcessing
public class BatchConfiguration {

Cela se terminera normalement.

Le profil n'est pas reflété

Cependant, lorsque je regarde dans la base de données après avoir exécuté le processus par lots, il n'y a pas de données, y compris la table des personnes. En regardant le journal des résultats de l'exécution, il semble que vous accédez à hsql, qui est en mémoire. À propos, la chaîne de l'art ASCII de Spring Boot n'apparaît pas non plus.

21:30:30.720 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.hsqldb.jdbc.JDBCConnection@2f8bc07b
21:30:30.724 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'hikariPoolDataSourceMetadataProvider'
21:30:30.724 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'hikariPoolDataSourceMetadataProvider'
21:30:32.546 [main] INFO org.springframework.batch.core.repository.support.JobRepositoryFactoryBean - No database type set, using meta data indicating: HSQL
21:30:33.032 [main] INFO org.springframework.jdbc.datasource.init.ScriptUtils - Executing SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql]
21:30:33.044 [main] INFO org.springframework.jdbc.datasource.init.ScriptUtils - Executed SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql] in 12 ms.
21:30:33.753 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.hsqldb.jdbc.JDBCConnection@17776a8: (connection evicted)

Je me suis demandé pourquoi il en était ainsi et j'ai comparé la classe CommandLineJobRunner avec la classe SpringApplication. Ce que j'ai trouvé ici, c'est que CommandLineJobRunner crée simplement un contexte Spring avec AnnotationConfigApplicationContext. Par conséquent, le bean est enregistré dans le conteneur, mais aucun autre comportement n'est attendu.

CommandLineJobRunner.java


	int start(String jobPath, String jobIdentifier, String[] parameters, Set<String> opts) {

		ConfigurableApplicationContext context = null;

		try {
			try {
				context = new AnnotationConfigApplicationContext(Class.forName(jobPath));
			} catch (ClassNotFoundException cnfe) {
				context = new ClassPathXmlApplicationContext(jobPath);
			}

En revanche, dans Spring Application, les fonctions liées à l'environnement sont initialisées. En outre, il semble que la chaîne artistique Spring Boot ASCII soit générée par la méthode printBanner dans l'instance d'environnement initialisée.

SplingApplicaton.java


	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments); //Les propriétés de l'environnement sont définies ici. application.La valeur des propriétés est définie dans la classe DataSourceProperties et définie dans la source de données.
			configureIgnoreBeanInfo(environment); 
			Banner printedBanner = printBanner(environment);

Dans la classe SpringApplication, une fois les différentes initialisations terminées, le travail est finalement exécuté à partir de la classe JobLauncherCommandLineRunner. Puisqu'il n'y a pas de processus d'initialisation SpringApplication dans la classe CommandLineJobRunner, les fonctions liées au profil ne peuvent pas être utilisées. Cette recherche s'est concentrée sur la fonction de profil pour répondre aux exigences, mais il peut y avoir d'autres fonctions qui ne peuvent pas être utilisées.

Considérez @ PropertySource

Il s'avère que application.properties, qui doit être chargé par défaut, n'est pas disponible, sans parler de la fonctionnalité du profil. Ainsi, en utilisant @ PropertySource, utilisez le fichier de propriétés J'ai envisagé la politique de spécification explicite.

BatchConfiguartion.java


@SpringBootApplication
@EnableBatchProcessing
@PropertySource("application.properties")
public class BatchConfiguration {
}

Ajoutez ensuite l'environnement à la fin de la clé application.properties.

application.properties


spring.batch.initialize-schema=ALWAYS
spring.datasource.username.development=root
spring.datasource.password.development=mysql
spring.datasource.url.development=jdbc:log4jdbc:mysql://127.0.0.1:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name.development=net.sf.log4jdbc.DriverSpy
spring.datasource.username.production=production_user
spring.datasource.password.production=production_password
spring.datasource.url.production=jdbc:log4jdbc:mysql://production.kasakaid.io:3306/kasakaidDB?useSSL=false
spring.datasource.driver-class-name.production=net.sf.log4jdbc.DriverSpy

Vous devriez pouvoir accéder aux propriétés système avec System.getEnv ("spring.profiles.active"). Je pense que l'information sur l'environnement peut être distinguée par ceci, mais je pense que cette méthode est très compliquée.

La clé est compliquée

Un grand nombre de touches similaires sont alignées et difficiles à distinguer

Jugez vous-même de l'environnement

À l'aide de la fonctionnalité Spring Boot, Spring choisira automatiquement les propriétés application.properties appropriées en fonction de votre profil pour déterminer votre environnement actuel. Cependant, avec cette méthode, l'implémenteur doit toujours être conscient de ce qu'est l'environnement. Les avantages de l'utilisation du framework sont grandement perdus. La mise en œuvre est comme ça.

SpringUtils.java


@Component
public SpringUtils {
    @Autowired
    Environment env;
    public String property(String key) {
        return env.getProperty(key + "." + System.getEnv("spring.profiles.active"));
    }
}

Vous devez faire ce que Spring fait automatiquement

Cette fois, ce qui ressort soudainement, c'est le paramétrage des propriétés de Datasource. Dans Spring Boot, la valeur de spring.datasource. * Dans application.properties est automatiquement définie dans Datasource. Cependant, cette méthode nécessite que vous définissiez explicitement la valeur de Datasource.

Configuration.java


    @Autowired
    SpringUtils springUtils;
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setDriverClassName(springUtils.property("spring.datasource.driver-class-name"));
        ds.setUsername(springUtils.property("spring.datasource.username"));
        ds.setPassword(springUtils.property("spring.datasource.password"));
        ds.setJdbcUrl(springUtils.property("spring.datasource.url"));
        return ds;
    }

Conclusion de CommandLineJobRunner

Comme mentionné ci-dessus, j'ai énuméré les inconvénients, y compris l'imagination. Il y a trois contenus répertoriés, mais il y a un problème que l'implémenteur doit penser à des choses qui ne devraient pas être considérées. De plus, au fur et à mesure que le développement progresse, d'autres inconvénients apparaîtront, et il n'y a aucune garantie que les facteurs à prendre en compte se heurteront et provoqueront des inconvénients fatals. Pour cette raison, je suis venu à l'idée qu'il vaut mieux arrêter d'exécuter Spring Batch avec CommandLineJobRunner.

Recommended Posts

Éléments à prendre en compte lors de l'exécution d'un travail spécifié à l'aide de Spring Batch
Précautions lors de l'utilisation de Spring AOP avec les classes de ressources Jersery
Je souhaite établir une connexion lorsqu'une base de données est créée à l'aide de Spring et MyBatis
Soumettre une tâche à AWS Batch avec Java (Eclipse)
Points à surveiller lors de la création d'un framework
Points à garder à l'esprit lors de l'utilisation de l'instruction if
Un mémorandum lors de la tentative de création d'une interface graphique à l'aide de JavaFX
[Introduction à Spring Boot] Soumettez un formulaire à l'aide de thymeleaf
Choses à surveiller lors de l'utilisation de Kmeans dans Deeplearning4j
Comment utiliser le référentiel de jobs en mémoire avec Spring Batch
8 choses à insérer dans DB en utilisant Spring Boot et JPA
Paramètres de lancement de tâche Spring Batch
Comment créer un hinadan pour un projet Spring Boot à l'aide de SPRING INITIALIZR
Éléments à prendre en compte lors du choix de l'architecture d'un nouveau système
Un moyen simple de créer une classe de mappage lors de l'utilisation de l'API
Choses à oublier lors de l'interception d'une requête avec Android WebView # shouldInterceptRequest
Une histoire à laquelle j'étais accro lors du test de l'API à l'aide de MockMVC
Un débutant Java a essayé de créer une application Web simple à l'aide de Spring Boot
Points à garder à l'esprit lors de l'utilisation d'Apache PDFBox® avec AWS Lambda
Étapes pour créer une application chameau simple avec les démarreurs Apache Camel Spring Boot
JSESSIONID n'a pas pu être attribué à l'URL lors de l'utilisation de Spring Security