Spring batch jpaPagingItemReader pourquoi certaines lignes ne sont pas lus?
Je 'm à l'aide de Spring Batch(3.0.1.De presse) /JPA et un HSQLBD serveur de base de données.
J'ai besoin de parcourir l'ensemble d'un tableau (à l'aide de la pagination) et les éléments de mise à jour (un par un). J'ai donc utilisé une jpaPagingItemReader. Mais quand je lance l'emploi, je peux voir que certaines lignes sont ignorés, et le nombre de sauté de lignes est égal à la taille de la page. Pour savoir si mon tableau comporte des lignes 12 et le jpaPagingItemReader.pagesize = 3 le travail de lecture : les lignes 1,2,3 puis lignes 7,8,9 (donc sauter les lignes 4,5,6)...
Pourriez-vous me dire quel est le problème dans mon code de configuration/ou peut-être un problème avec HSQLDB de pagination?
Ci-dessous mon code:
[MODIFIER] : Le problème est avec mon ItemProcessor qui effectue la modification de la Pojo Entités. Depuis JPAPagingItemReader fait une chasse d'eau entre chaque lecture, les Entités sont mises à jour ((c'est ce que je veux) . Mais il semble que le curseur de la pagination est également incrémenté (comme on peut le voir dans le journal: l'ID de ligne 4, 5 et 6 ont été ignorés). Comment puis-je gérer ce problème ?
@Configuration
@EnableBatchProcessing(modular=true)
public class AppBatchConfig {
@Inject
private InfrastructureConfiguration infrastructureConfiguration;
@Inject private JobBuilderFactory jobs;
@Inject private StepBuilderFactory steps;
@Bean public Job job() {
return jobs.get("Myjob1").start(step1()).build();
}
@Bean public Step step1() {
return steps.get("step1")
.<SNUserPerCampaign, SNUserPerCampaign> chunk(0)
.reader(reader()).processor(processor()).build();
}
@Bean(destroyMethod = "")
@JobScope
public ItemStreamReader<SNUserPerCampaign> reader() String trigramme) {
JpaPagingItemReader reader = new JpaPagingItemReader();
reader.setEntityManagerFactory(infrastructureConfiguration.getEntityManagerFactory());
reader.setQueryString("select t from SNUserPerCampaign t where t.isactive=true");
reader.setPageSize(3));
return reader;
}
@Bean @JobScope
public ItemProcessor<SNUserPerCampaign, SNUserPerCampaign> processor() {
return new MyItemProcessor();
}
}
@Configuration
@EnableBatchProcessing
public class StandaloneInfrastructureConfiguration implements InfrastructureConfiguration {
@Inject private EntityManagerFactory emf;
@Override
public EntityManagerFactory getEntityManagerFactory() {
return emf;
}
}
de mon ItemProcessor:
@Override
public SNUserPerCampaign process(SNUserPerCampaign item) throws Exception {
//do some stuff …
//then if (condition) update the Entity pojo :
item.setModificationDate(new Timestamp(System.currentTimeMillis());
item.setIsactive = false;
}
de Printemps fichier de configuration xml:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001/MYAppDB" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
trace/log résumé :
11:16:05.728 TRACE MyItemProcessor - item processed: snUserInternalId=1]
11:16:06.038 TRACE MyItemProcessor - item processed: snUserInternalId=2]
11:16:06.350 TRACE MyItemProcessor - item processed: snUserInternalId=3]
11:16:06.674 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.677 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.679 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.681 DEBUG SQL- select ...etc... from SNUSER_CAMPAIGN snuserperc0_
11:16:06.687 TRACE MyItemProcessor - item processed: snUserInternalId=7]
11:16:06.998 TRACE MyItemProcessor - item processed: snUserInternalId=8]
11:16:07.314 TRACE MyItemProcessor - item processed: snUserInternalId=9]
J'ai réalisé que le problème était mon ItemProcessor que les changements de l'entité pojo, j'ai édité ma question par l'ajout de traces
Je suis un peu confus. Les articles retournés de la
JpaPagingItemReader
sont détachés de sorte qu'ils ne devraient pas être mis à jour sans explicite de l'écriture. Pouvez-vous fournir tout le travail de configuration?Je vais essayer d'ajouter plus de détails demain. De toute façon, ce que je peux voir au Printemps JPAPagingItemreader du code source, c'est que les éléments semble être détaché lors de la lecture de la page suivante (en faisant une couleur claire pour le contexte). C'est pourquoi je n'ai pas besoin d'un ItemWritter persister ma mise à jour. Un détail important peut-être que dans mon Élément de Processeur-je mettre à jour une valeur de type boolean qui est dans mon SÉLECTIONNEZ clause where; je ne sais pas si cela peut modifier la pagination du curseur.
La chasse d'eau et clair, c'est juste un nettoyage au début. À la fin de
JpaPagingItemReader
nous boucle à travers les éléments et explicitement les détacher, ou de valider la transaction, de sorte qu'ils sont détachés. Dans les deux cas, les entités retournées à partir de la JpaPagingItemReader
doit être dans l'état détaché. Vous pouvez poster la configuration de votre travail?
OriginalL'auteur ThierryC | 2014-10-22
Vous devez vous connecter pour publier un commentaire.
org.springframework.lot.de l'élément.la base de données.JpaPagingItemReader crée est propre entityManager instance
(à partir de org.springframework.lot.de l'élément.la base de données.JpaPagingItemReader#doOpen) :
Si vous êtes à l'intérieur d'une transaction, comme cela semble être le lecteur, les entités ne sont pas détachés
(à partir de org.springframework.lot.de l'élément.la base de données.JpaPagingItemReader#doReadPage):
Pour cette raison, lorsque vous mettez à jour un élément dans un processeur ou de l'écrivain, ce point est toujours géré par le reader's entityManager.
Lorsque l'élément lecteur lit le prochain bloc de données, il vide le contexte de la base de données.
Donc, si on regarde votre cas, après le premier bloc de données de processus, nous avons en base de données:
org.springframework.lot.de l'élément.la base de données.JpaPagingItemReader utilise limit & offset pour récupérer paginé de données. Donc, la prochaine sélectionnez créé par le lecteur ressemble à :
Lecteur va manquer les éléments avec l'id 4,5,6, parce qu'ils sont désormais les premières lignes extraites d'une base de données.
Ce que vous pouvez faire, comme une solution de contournement est d'utiliser jdbc mise en œuvre (org.springframework.lot.de l'élément.la base de données.JdbcPagingItemReader), car il n'a pas de limite d'utilisation & offset. Il est basé sur une colonne triée (en général, la colonne id), de sorte que vous ne manquerez pas de toutes les données.
Bien sûr, vous devrez mettre à jour vos données dans l'écrivain (à l'aide de JPA ou pure JDBC mise en œuvre)
Lecteur sera plus prolixe:
Pour une clé composite, vous pouvez utiliser
org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean#setSortKeys
. Il fonctionne avec alias depuis Michael Minella est fix (jira.printemps.io/parcourir/LOT-2079 , merci Michael!).La déclaration ci-dessus de ne pas être détaché est incorrect. Le tx.commit() doit laisser toutes les entités associées à la transaction (ce qui a été créé au début de l'doReadPage méthode) s'est détachée.
Je m'y attendais trop, mais en utilisant le débogueur, je peux voir que le lecteur de l'entityManager contient des entités de mise à jour du processeur/écrivain. Dans mon cas, les entités ne sont pas détachés.
Je suis d'accord, il semble que, même après la JpaPagingItemReader commettre, les articles sont toujours dans l'entityManager et sont mises à jour lors de la lecture de la page suivante, dans mon fichier de log, je ne vois pas certains requête SQL qui pourrait correspondre à une "re-joint" demande.
OriginalL'auteur Manuel Verriez
Un couple de choses à noter:
JpaPaginingItemReader
sont détachés. Nous accomplissons cela de deux façons. Nous avons créer une transaction avant l'interrogation de la page, puis sur valider la transaction (qui se détache de toutes les entités associées à laEntityManager
pour que la transaction) ou de nous appeler explicitemententityManager.detach
. Nous faisons cela afin que les fonctionnalités comme réessayer et ignorer peut être correctement effectuée.//do some stuff
article, votre article est de se re-joint qui est pourquoi la mise à jour est en cours. Cependant, sans être en mesure de voir que le code, je ne peux pas en être sûr.ItemWriter
devrait être fait. En fait, je considère que c'est un bug que nous n'avons pas besoin d'unItemWriter
lors de l'utilisation de java config (nous n'avons pour le XML).*PagingItemReader
s. Ils exécutent indépendant des requêtes pour chaque page de données. Donc, si vous mettez à jour les données sous-jacentes entre chaque page, il peut avoir un impact sur les articles retournés dans les futures pages. Par exemple, si ma pagination requête spécifiewhere val1 > 4
et j'ai un record que val1 était de 1 à 5, dans le bloc 2, cet élément peut être retourné car il remplit les critères. Si vous avez besoin de mettre à jour les valeurs qui sont dans votre clause where (ce qui influe sur ce qui tombe dans le jeu de données que vous auriez du traitement), il est préférable d'ajouter un traité drapeau de quelque sorte que vous pouvez interroger par la place.Pouvez-vous ajouter de tout votre code pour le processeur?
C'est trop gros, mais hier, j'ai pu reproduire le problème, même quand je mets presque tous dans les commentaires. L'élément processeur est en utilisant les getter et setter de la Pojo de l'entité; et le problème est survenu parce que, comme mentionné, j'ai modifié un domaine qui a été dans la sélection de demande de critères.
OriginalL'auteur Michael Minella
J'ai eu le même problème avec les lignes ignorées basé sur la pageSize.
Si j'ai pageSize fixé à 2 par exemple, il serait lire 2, ignorer 2, lire 2, ignorer 2 etc.
J'ai été la construction d'un démon processeur pour interroger une 'Demande' table de base de données pour les enregistrements à une "Attente De traitement". Le démon est conçu pour fonctionner à jamais dans l'arrière-plan.
J'ai eu un "statut" de terrain qui a été défini dans le @NamedQuery et permettrait de sélectionner les enregistrements dont le statut a été '10':en Attente de traitement. Après que le dossier a été traité, le champ de statut sera mis à jour à '20':Erreur ou '30':le Succès.
Cela s'est avéré pour être la cause du problème - j'ai été mise à jour d'un champ qui a été défini dans la requête. Si j'ai mis en place un "processedField" et mis à jour qu'au lieu de 'l'état' champ, alors pas de problème, tous les enregistrements de lecture.
Comme une solution possible pour mettre à jour le champ d'état, je setMaxItemCount être le même que le PageSize; cette mise à jour les enregistrements correctement avant l'étape d'achèvement. J'ai alors gardez l'exécution de l'étape jusqu'à ce qu'une demande est faite pour arrêter le démon. OK, probablement pas le moyen le plus efficace de le faire (mais je suis toujours en bénéficiant de la facilité d'utilisation de JPA) mais je pense qu'il serait probablement mieux d'utiliser JdbcPagingItemReader (décrites ci – dessus (merci!). Avis sur la meilleure approche à ce lot d'interrogation de la base de données problème serait la bienvenue 🙂
OriginalL'auteur Mark Plumridge
J'ai connu le même cas, mon lecteur est un JpaPagingItemReader qu'interrogé sur un champ qui a été mis à jour à l'écrivain. Par conséquent sauter la moitié des éléments qui doivent être mis à jour, en raison de la fenêtre de la page de progresser tandis que les articles déjà lus n'étaient pas dans le lecteur de portée plus.
La solution la plus simple pour moi a été de remplacer getPage méthode sur la JpaPagingItemReader retourne toujours la première page.
OriginalL'auteur Yves-Marie L.