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.
En tant qu'exigence pour les applications par lots, je voulais au moins atteindre ce qui suit.
É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.
__ 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:
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
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é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]
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
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();
}
@ SpringBootApplication
Je me suis demandé ce qui était arrivé à cela, mais il n'y a que deux annotations dans la classe BatchConfiguration:
@EnableBatchProcessing
@Configuration
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.
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.
@ 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.
Un grand nombre de touches similaires sont alignées et difficiles à distinguer
À 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"));
}
}
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;
}
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.