Remove deprecated APIs scheduled for removal in v6

Resolves #4819
This commit is contained in:
Mahmoud Ben Hassine
2025-04-29 08:57:30 +02:00
parent 9d38aceade
commit 43ac1f12cf
79 changed files with 1635 additions and 3694 deletions

View File

@@ -111,11 +111,6 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar {
beanDefinitionBuilder.addPropertyReference("serializer", executionContextSerializerRef);
}
String lobHandlerRef = batchAnnotation.lobHandlerRef();
if (registry.containsBeanDefinition(lobHandlerRef)) {
beanDefinitionBuilder.addPropertyReference("lobHandler", lobHandlerRef);
}
String conversionServiceRef = batchAnnotation.conversionServiceRef();
if (registry.containsBeanDefinition(conversionServiceRef)) {
beanDefinitionBuilder.addPropertyReference("conversionService", conversionServiceRef);
@@ -178,11 +173,6 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar {
beanDefinitionBuilder.addPropertyReference("serializer", executionContextSerializerRef);
}
String lobHandlerRef = batchAnnotation.lobHandlerRef();
if (registry.containsBeanDefinition(lobHandlerRef)) {
beanDefinitionBuilder.addPropertyReference("lobHandler", lobHandlerRef);
}
String conversionServiceRef = batchAnnotation.conversionServiceRef();
if (registry.containsBeanDefinition(conversionServiceRef)) {
beanDefinitionBuilder.addPropertyReference("conversionService", conversionServiceRef);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -238,14 +238,6 @@ public @interface EnableBatchProcessing {
*/
String jobKeyGeneratorRef() default "jobKeyGenerator";
/**
* The large object handler to use in job repository and job explorer.
* @return the bean name of the lob handler to use. Defaults to {@literal lobHandler}.
* @deprecated Since 5.2 with no replacement. Scheduled for removal in v6
*/
@Deprecated(since = "5.2.0", forRemoval = true)
String lobHandlerRef() default "lobHandler";
/**
* The type of large objects.
* @return the type of large objects.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,8 +67,6 @@ import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
@@ -85,7 +83,8 @@ import org.springframework.transaction.annotation.Isolation;
* <li>a {@link JobLauncher} named "jobLauncher"</li>
* <li>a {@link JobRegistry} named "jobRegistry"</li>
* <li>a {@link JobOperator} named "JobOperator"</li>
* <li>a {@link JobRegistryBeanPostProcessor} named "jobRegistryBeanPostProcessor"</li>
* <li>a {@link JobRegistrySmartInitializingSingleton} named
* "jobRegistrySmartInitializingSingleton"</li>
* <li>a {@link org.springframework.batch.core.scope.StepScope} named "stepScope"</li>
* <li>a {@link org.springframework.batch.core.scope.JobScope} named "jobScope"</li>
* </ul>
@@ -137,7 +136,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
jobRepositoryFactoryBean.setSerializer(getExecutionContextSerializer());
jobRepositoryFactoryBean.setConversionService(getConversionService());
jobRepositoryFactoryBean.setJdbcOperations(getJdbcOperations());
jobRepositoryFactoryBean.setLobHandler(getLobHandler());
jobRepositoryFactoryBean.setCharset(getCharset());
jobRepositoryFactoryBean.setMaxVarCharLength(getMaxVarCharLength());
jobRepositoryFactoryBean.setIsolationLevelForCreateEnum(getIsolationLevelForCreate());
@@ -150,17 +148,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
}
}
/**
* Define a job launcher.
* @return a job launcher
* @throws BatchConfigurationException if unable to configure the default job launcher
* @deprecated Since 5.2. Use {@link #jobLauncher(JobRepository)} instead
*/
@Deprecated(forRemoval = true)
public JobLauncher jobLauncher() throws BatchConfigurationException {
return jobLauncher(jobRepository());
}
/**
* Define a job launcher bean.
* @param jobRepository the job repository
@@ -191,7 +178,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
jobExplorerFactoryBean.setJobKeyGenerator(getJobKeyGenerator());
jobExplorerFactoryBean.setCharset(getCharset());
jobExplorerFactoryBean.setTablePrefix(getTablePrefix());
jobExplorerFactoryBean.setLobHandler(getLobHandler());
jobExplorerFactoryBean.setConversionService(getConversionService());
jobExplorerFactoryBean.setSerializer(getExecutionContextSerializer());
try {
@@ -208,18 +194,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
return new MapJobRegistry();
}
/**
* Define a job operator.
* @return a job operator
* @throws BatchConfigurationException if unable to configure the default job operator
* @deprecated Since 5.2. Use
* {@link #jobOperator(JobRepository, JobExplorer, JobRegistry, JobLauncher)} instead
*/
@Deprecated(forRemoval = true)
public JobOperator jobOperator() throws BatchConfigurationException {
return jobOperator(jobRepository(), jobExplorer(), jobRegistry(), jobLauncher());
}
/**
* Define a job operator bean.
* @param jobRepository a job repository
@@ -249,26 +223,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
}
}
/**
* Defines a {@link JobRegistryBeanPostProcessor}.
* @return a {@link JobRegistryBeanPostProcessor}
* @throws BatchConfigurationException if unable to register the bean
* @since 5.1
* @deprecated Use {@link #jobRegistrySmartInitializingSingleton(JobRegistry)} instead
*/
@Deprecated(forRemoval = true)
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() throws BatchConfigurationException {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry());
try {
jobRegistryBeanPostProcessor.afterPropertiesSet();
return jobRegistryBeanPostProcessor;
}
catch (Exception e) {
throw new BatchConfigurationException("Unable to configure the default job registry BeanPostProcessor", e);
}
}
/**
* Define a {@link JobRegistrySmartInitializingSingleton} bean.
* @param jobRegistry the job registry to populate
@@ -388,17 +342,6 @@ public class DefaultBatchConfiguration implements ApplicationContextAware {
return StandardCharsets.UTF_8;
}
/**
* A special handler for large objects. The default is usually fine, except for some
* (usually older) versions of Oracle.
* @return the {@link LobHandler} to use
* @deprecated Since 5.2 with no replacement. Scheduled for removal in v6
*/
@Deprecated(since = "5.2.0", forRemoval = true)
protected LobHandler getLobHandler() {
return new DefaultLobHandler();
}
/**
* Return the {@link JdbcOperations}. If this property is not overridden, a new
* {@link JdbcTemplate} will be created for the configured data source by default.

View File

@@ -1,176 +0,0 @@
/*
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.support;
import java.util.Collection;
import java.util.HashSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.util.Assert;
/**
* A {@link BeanPostProcessor} that registers {@link Job} beans with a
* {@link JobRegistry}. Include a bean of this type along with your job configuration and
* use the same {@link JobRegistry} as a {@link JobLocator} when you need to locate a
* {@link Job} to launch.
* <p>
* An alternative to this class is {@link JobRegistrySmartInitializingSingleton}, which is
* recommended in cases where this class may cause early bean initializations. You must
* include at most one of either of them as a bean.
*
* @deprecated since 5.2 in favor of {@link JobRegistrySmartInitializingSingleton}.
* @author Dave Syer
* @author Mahmoud Ben Hassine
*
*/
@Deprecated(since = "5.2")
public class JobRegistryBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware, InitializingBean, DisposableBean {
private static final Log logger = LogFactory.getLog(JobRegistryBeanPostProcessor.class);
// It doesn't make sense for this to have a default value...
private JobRegistry jobRegistry = null;
private final Collection<String> jobNames = new HashSet<>();
private String groupName = null;
private DefaultListableBeanFactory beanFactory;
/**
* The group name for jobs registered by this component. Optional (defaults to null,
* which means that jobs are registered with their bean names). Useful where there is
* a hierarchy of application contexts all contributing to the same
* {@link JobRegistry}: child contexts can then define an instance with a unique group
* name to avoid clashes between job names.
* @param groupName the groupName to set
*/
public void setGroupName(String groupName) {
this.groupName = groupName;
}
/**
* Injection setter for {@link JobRegistry}.
* @param jobRegistry the jobConfigurationRegistry to set
*/
public void setJobRegistry(JobRegistry jobRegistry) {
this.jobRegistry = jobRegistry;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof DefaultListableBeanFactory) {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
}
/**
* Make sure the registry is set before use.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(jobRegistry != null, "JobRegistry must not be null");
}
/**
* Unregister all the {@link Job} instances that were registered by this post
* processor.
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
@Override
public void destroy() throws Exception {
for (String name : jobNames) {
if (logger.isDebugEnabled()) {
logger.debug("Unregistering job: " + name);
}
jobRegistry.unregister(name);
}
jobNames.clear();
}
/**
* If the bean is an instance of {@link Job}, then register it.
* @throws FatalBeanException if there is a {@link DuplicateJobException}.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
* java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Job job) {
try {
String groupName = this.groupName;
if (beanFactory != null && beanFactory.containsBean(beanName)) {
groupName = getGroupName(beanFactory.getBeanDefinition(beanName), job);
}
job = groupName == null ? job : new GroupAwareJob(groupName, job);
ReferenceJobFactory jobFactory = new ReferenceJobFactory(job);
String name = jobFactory.getJobName();
if (logger.isDebugEnabled()) {
logger.debug("Registering job: " + name);
}
jobRegistry.register(jobFactory);
jobNames.add(name);
}
catch (DuplicateJobException e) {
throw new FatalBeanException("Cannot register job configuration", e);
}
return job;
}
return bean;
}
/**
* Determine a group name for the job to be registered. The default implementation
* returns the {@link #setGroupName(String) groupName} configured. Provides an
* extension point for specialised subclasses.
* @param beanDefinition the bean definition for the job
* @param job the job
* @return a group name for the job (or null if not needed)
*/
protected String getGroupName(BeanDefinition beanDefinition, Job job) {
return groupName;
}
/**
* Do nothing.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
* java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,9 +42,6 @@ import org.springframework.util.Assert;
* {@link JobRegistry}. Include a bean of this type along with your job configuration and
* use the same {@link JobRegistry} as a {@link JobLocator} when you need to locate a
* {@link Job} to launch.
* <p>
* This class is an alternative to {@link JobRegistryBeanPostProcessor} and prevents early
* bean initializations. You must include at most one of either of them as a bean.
*
* @author Henning Pöttker
* @since 5.1.1

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2022 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -77,8 +77,6 @@ public class JobRepositoryParser extends AbstractSingleBeanDefinitionParser {
String maxVarCharLength = element.getAttribute("max-varchar-length");
String lobHandler = element.getAttribute("lob-handler");
String serializer = element.getAttribute("serializer");
String conversionService = element.getAttribute("conversion-service");
@@ -97,9 +95,6 @@ public class JobRepositoryParser extends AbstractSingleBeanDefinitionParser {
if (StringUtils.hasText(tablePrefix)) {
builder.addPropertyValue("tablePrefix", tablePrefix);
}
if (StringUtils.hasText(lobHandler)) {
builder.addPropertyReference("lobHandler", lobHandler);
}
if (StringUtils.hasText(maxVarCharLength)) {
builder.addPropertyValue("maxVarCharLength", maxVarCharLength);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -185,8 +185,6 @@ public class StepParserStepFactoryBean<I, O> implements FactoryBean<Step>, BeanN
private TaskExecutor taskExecutor;
private Integer throttleLimit;
private ItemReader<? extends I> itemReader;
private ItemProcessor<? super I, ? extends O> itemProcessor;
@@ -473,9 +471,6 @@ public class StepParserStepFactoryBean<I, O> implements FactoryBean<Step>, BeanN
}
builder.taskExecutor(taskExecutor);
if (throttleLimit != null) {
builder.throttleLimit(throttleLimit);
}
builder.transactionManager(transactionManager);
if (transactionTimeout != null || propagation != null || isolation != null
|| noRollbackExceptionClasses != null) {
@@ -992,19 +987,6 @@ public class StepParserStepFactoryBean<I, O> implements FactoryBean<Step>, BeanN
this.taskExecutor = taskExecutor;
}
/**
* Public setter for the throttle limit. This limits the number of tasks queued for
* concurrent processing to prevent thread pools from being overwhelmed. Defaults to
* {@link TaskExecutorRepeatTemplate#DEFAULT_THROTTLE_LIMIT}.
* @param throttleLimit The throttle limit to set.
* @deprecated since 5.0, scheduled for removal in 6.0. This API is not intended for
* end users anyway. It is only used by the XML namespace parser.
*/
@Deprecated(since = "5.0", forRemoval = true)
public void setThrottleLimit(Integer throttleLimit) {
this.throttleLimit = throttleLimit;
}
/**
* @param itemReader The {@link ItemReader} to set.
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,6 @@ import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
@@ -81,8 +80,6 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple
private JobKeyGenerator jobKeyGenerator;
private LobHandler lobHandler;
private ExecutionContextSerializer serializer;
private Charset charset = StandardCharsets.UTF_8;
@@ -138,18 +135,6 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple
this.jobKeyGenerator = jobKeyGenerator;
}
/**
* The lob handler to use when saving {@link ExecutionContext} instances. Defaults to
* {@code null}, which works for most databases.
* @param lobHandler Large object handler for saving an
* {@link org.springframework.batch.item.ExecutionContext}.
* @deprecated Since 5.2 with no replacement. Scheduled for removal in v6
*/
@Deprecated(since = "5.2.0", forRemoval = true)
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
/**
* Sets the {@link Charset} to use when deserializing the execution context. Defaults
* to "UTF-8". Must not be {@code null}.
@@ -210,7 +195,6 @@ public class JobExplorerFactoryBean extends AbstractJobExplorerFactoryBean imple
protected ExecutionContextDao createExecutionContextDao() throws Exception {
JdbcExecutionContextDao dao = new JdbcExecutionContextDao();
dao.setJdbcTemplate(jdbcOperations);
dao.setLobHandler(lobHandler);
dao.setTablePrefix(tablePrefix);
dao.setSerializer(serializer);
dao.setCharset(charset);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,16 +30,6 @@ import org.springframework.batch.core.repository.JobRepository;
*/
public class JobBuilder extends JobBuilderHelper<JobBuilder> {
/**
* Create a new builder for a job with the given name.
* @param name the name of the job
* @deprecated use {@link JobBuilder#JobBuilder(String, JobRepository)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public JobBuilder(String name) {
super(name);
}
/**
* Create a new builder for a job with the given name.
* @param name the name of the job

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,17 +54,6 @@ public abstract class JobBuilderHelper<B extends JobBuilderHelper<B>> {
private final CommonJobProperties properties;
/**
* Create a new {@link JobBuilderHelper}.
* @param name the job name
* @deprecated use {@link JobBuilderHelper#JobBuilderHelper(String, JobRepository)}
*/
@Deprecated(since = "5.1", forRemoval = true)
public JobBuilderHelper(String name) {
this.properties = new CommonJobProperties();
properties.name = name;
}
/**
* Create a new {@link JobBuilderHelper}.
* @param name the job name
@@ -110,20 +99,6 @@ public abstract class JobBuilderHelper<B extends JobBuilderHelper<B>> {
return result;
}
/**
* Sets the job repository for the job.
* @param jobRepository the job repository (mandatory)
* @return this to enable fluent chaining
* @deprecated use {@link JobBuilderHelper#JobBuilderHelper(String, JobRepository)}
*/
@Deprecated(since = "5.1", forRemoval = true)
public B repository(JobRepository jobRepository) {
properties.jobRepository = jobRepository;
@SuppressWarnings("unchecked")
B result = (B) this;
return result;
}
/**
* Sets the job observation convention.
* @param observationConvention the job observation convention (optional)

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.listener;
import org.springframework.batch.core.ChunkListener;
import org.springframework.batch.core.scope.context.ChunkContext;
/**
* Basic support implementation of {@link ChunkListener}
*
* @author Lucas Ward
* @author Michael Minella
* @deprecated as of 5.0, in favor of the default methods on the {@link ChunkListener}
*/
@Deprecated
public class ChunkListenerSupport implements ChunkListener {
@Override
public void afterChunk(ChunkContext context) {
}
@Override
public void beforeChunk(ChunkContext context) {
}
@Override
public void afterChunkError(ChunkContext context) {
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.listener;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
/**
* @author Dave Syer
* @deprecated as of 5.0, in favor of the default methods on the
* {@link JobExecutionListener}
*/
@Deprecated
public class JobExecutionListenerSupport implements JobExecutionListener {
@Override
public void afterJob(JobExecution jobExecution) {
}
@Override
public void beforeJob(JobExecution jobExecution) {
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.listener;
import org.springframework.batch.core.SkipListener;
/**
* Basic no-op implementations of all {@link SkipListener} implementations.
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @deprecated as of v5.0 in favor of the default methods in {@link SkipListener}.
*
*/
@Deprecated
public class SkipListenerSupport<T, S> implements SkipListener<T, S> {
@Override
public void onSkipInRead(Throwable t) {
}
@Override
public void onSkipInWrite(S item, Throwable t) {
}
@Override
public void onSkipInProcess(T item, Throwable t) {
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.listener;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.lang.Nullable;
/**
* @author Dave Syer
* @deprecated as of 5.0, in favor of the default methods on the
* {@link StepExecutionListener}
*/
@Deprecated
public class StepExecutionListenerSupport implements StepExecutionListener {
@Nullable
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
@Override
public void beforeStep(StepExecution stepExecution) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2024 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,8 +40,6 @@ import org.springframework.batch.item.ExecutionContext;
import org.springframework.core.serializer.Serializer;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
@@ -110,8 +108,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
private int shortContextLength = DEFAULT_MAX_VARCHAR_LENGTH;
private LobHandler lobHandler = new DefaultLobHandler();
private ExecutionContextSerializer serializer = new DefaultExecutionContextSerializer();
private final Lock lock = new ReentrantLock();
@@ -268,15 +264,6 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
getJdbcTemplate().update(getQuery(DELETE_STEP_EXECUTION_CONTEXT), stepExecution.getId());
}
/**
* @deprecated Since 5.2 with no replacement. Scheduled for removal in v6
* @param lobHandler the lob handler to use
*/
@Deprecated(since = "5.2.0", forRemoval = true)
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
@@ -306,7 +293,7 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
getJdbcTemplate().update(getQuery(sql), ps -> {
ps.setString(1, shortContext);
if (longContext != null) {
lobHandler.getLobCreator().setClobAsString(ps, 2, longContext);
ps.setString(2, longContext);
}
else {
ps.setNull(2, getClobTypeToUse());
@@ -342,7 +329,7 @@ public class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao implem
}
ps.setString(1, shortContext);
if (longContext != null) {
lobHandler.getLobCreator().setClobAsString(ps, 2, longContext);
ps.setString(2, longContext);
}
else {
ps.setNull(2, getClobTypeToUse());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -284,17 +284,6 @@ public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements
getJdbcTemplate().update(getQuery(DELETE_JOB_INSTANCE), jobInstance.getId());
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when generating primary
* keys for {@link JobInstance} instances.
* @param jobIncrementer the {@link DataFieldMaxValueIncrementer}
* @deprecated as of v5.0 in favor of using the {@link #setJobInstanceIncrementer}
*/
@Deprecated
public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
this.setJobInstanceIncrementer(jobIncrementer);
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when generating primary
* keys for {@link JobInstance} instances.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,8 +56,6 @@ import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -95,8 +93,6 @@ public class JobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean i
private int maxVarCharLengthForShortContext = AbstractJdbcBatchMetadataDao.DEFAULT_SHORT_CONTEXT_LENGTH;
private LobHandler lobHandler;
private ExecutionContextSerializer serializer;
private Integer clobType;
@@ -124,19 +120,6 @@ public class JobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean i
this.serializer = serializer;
}
/**
* A special handler for large objects. The default is usually fine, except for some
* (usually older) versions of Oracle. The default is determined from the data base
* type.
* @param lobHandler the {@link LobHandler} to set
* @deprecated Since 5.2 with no replacement. Scheduled for removal in v6
* @see LobHandler
*/
@Deprecated(since = "5.2.0", forRemoval = true)
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
/**
* Public setter for the length of long string columns in database. Do not set this if
* you haven't modified the schema. Note this value will be used for the exit message
@@ -276,10 +259,6 @@ public class JobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean i
}
}
if (lobHandler == null && databaseType.equalsIgnoreCase(DatabaseType.ORACLE.toString())) {
lobHandler = new DefaultLobHandler();
}
if (serializer == null) {
serializer = new DefaultExecutionContextSerializer();
}
@@ -355,10 +334,6 @@ public class JobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean i
dao.setSerializer(serializer);
dao.setCharset(charset);
if (lobHandler != null) {
dao.setLobHandler(lobHandler);
}
dao.afterPropertiesSet();
dao.setShortContextLength(this.maxVarCharLengthForShortContext);
return dao;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,8 +67,6 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
private ExceptionHandler exceptionHandler = new DefaultExceptionHandler();
private int throttleLimit = TaskExecutorRepeatTemplate.DEFAULT_THROTTLE_LIMIT;
private TaskExecutor taskExecutor;
public AbstractTaskletStepBuilder(StepBuilderHelper<?> parent) {
@@ -88,7 +86,6 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
this.transactionAttribute = parent.transactionAttribute;
this.streams.addAll(parent.streams);
this.exceptionHandler = parent.exceptionHandler;
this.throttleLimit = parent.throttleLimit;
this.taskExecutor = parent.taskExecutor;
}
@@ -125,7 +122,6 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
if (taskExecutor != null) {
TaskExecutorRepeatTemplate repeatTemplate = new TaskExecutorRepeatTemplate();
repeatTemplate.setTaskExecutor(taskExecutor);
repeatTemplate.setThrottleLimit(throttleLimit);
stepOperations = repeatTemplate;
}
@@ -210,24 +206,6 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
return self();
}
/**
* In the case of an asynchronous {@link #taskExecutor(TaskExecutor)} the number of
* concurrent tasklet executions can be throttled (beyond any throttling provided by a
* thread pool). The throttle limit should be less than the data source pool size used
* in the job repository for this step.
* @param throttleLimit maximum number of concurrent tasklet executions allowed
* @return this for fluent chaining
* @deprecated with no replacement since 5.0, scheduled for removal in 6.0. Use a
* custom {@link RepeatOperations} implementation (based on a {@link TaskExecutor}
* with a bounded task queue) and set it on the step with
* {@link #stepOperations(RepeatOperations)}.
*/
@Deprecated(since = "5.0", forRemoval = true)
public B throttleLimit(int throttleLimit) {
this.throttleLimit = throttleLimit;
return self();
}
/**
* Sets the exception handler to use in the case of tasklet failures. Default is to
* rethrow everything.
@@ -302,11 +280,6 @@ public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBu
return taskExecutor;
}
@Deprecated(since = "5.0", forRemoval = true)
protected int getThrottleLimit() {
return throttleLimit;
}
protected TransactionAttribute getTransactionAttribute() {
return transactionAttribute;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,16 +34,6 @@ import org.springframework.transaction.PlatformTransactionManager;
*/
public class StepBuilder extends StepBuilderHelper<StepBuilder> {
/**
* Initialize a step builder for a step with the given name.
* @param name the name of the step
* @deprecated use {@link StepBuilder#StepBuilder(String, JobRepository)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public StepBuilder(String name) {
super(name);
}
/**
* Initialize a step builder for a step with the given name and job repository.
* @param name the name of the step
@@ -54,17 +44,6 @@ public class StepBuilder extends StepBuilderHelper<StepBuilder> {
super(name, jobRepository);
}
/**
* Build a step with a custom tasklet, not necessarily item processing.
* @param tasklet a tasklet
* @return a {@link TaskletStepBuilder}
* @deprecated use {@link StepBuilder#tasklet(Tasklet, PlatformTransactionManager)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public TaskletStepBuilder tasklet(Tasklet tasklet) {
return new TaskletStepBuilder(this).tasklet(tasklet);
}
/**
* Build a step with a custom tasklet, not necessarily item processing.
* @param tasklet a tasklet
@@ -76,27 +55,6 @@ public class StepBuilder extends StepBuilderHelper<StepBuilder> {
return new TaskletStepBuilder(this).tasklet(tasklet, transactionManager);
}
/**
* Build a step that processes items in chunks with the size provided. To extend the
* step to being fault tolerant, call the {@link SimpleStepBuilder#faultTolerant()}
* method on the builder. In most cases you will want to parameterize your call to
* this method, to preserve the type safety of your readers and writers, e.g.
*
* <pre>
* new StepBuilder(&quot;step1&quot;).&lt;Order, Ledger&gt; chunk(100).reader(new OrderReader()).writer(new LedgerWriter())
* // ... etc.
* </pre>
* @param chunkSize the chunk size (commit interval)
* @return a {@link SimpleStepBuilder}
* @param <I> the type of item to be processed as input
* @param <O> the type of item to be output
* @deprecated use {@link StepBuilder#chunk(int, PlatformTransactionManager)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public <I, O> SimpleStepBuilder<I, O> chunk(int chunkSize) {
return new SimpleStepBuilder<I, O>(this).chunk(chunkSize);
}
/**
* Build a step that processes items in chunks with the size provided. To extend the
* step to being fault tolerant, call the {@link SimpleStepBuilder#faultTolerant()}
@@ -119,29 +77,6 @@ public class StepBuilder extends StepBuilderHelper<StepBuilder> {
return new SimpleStepBuilder<I, O>(this).transactionManager(transactionManager).chunk(chunkSize);
}
/**
* Build a step that processes items in chunks with the completion policy provided. To
* extend the step to being fault tolerant, call the
* {@link SimpleStepBuilder#faultTolerant()} method on the builder. In most cases you
* will want to parameterize your call to this method, to preserve the type safety of
* your readers and writers, e.g.
*
* <pre>
* new StepBuilder(&quot;step1&quot;).&lt;Order, Ledger&gt; chunk(100).reader(new OrderReader()).writer(new LedgerWriter())
* // ... etc.
* </pre>
* @param completionPolicy the completion policy to use to control chunk processing
* @return a {@link SimpleStepBuilder}
* @param <I> the type of item to be processed as input
* @param <O> the type of item to be output
* @deprecated use
* {@link StepBuilder#chunk(CompletionPolicy, PlatformTransactionManager)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public <I, O> SimpleStepBuilder<I, O> chunk(CompletionPolicy completionPolicy) {
return new SimpleStepBuilder<I, O>(this).chunk(completionPolicy);
}
/**
* Build a step that processes items in chunks with the completion policy provided. To
* extend the step to being fault tolerant, call the

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,17 +53,6 @@ public abstract class StepBuilderHelper<B extends StepBuilderHelper<B>> {
protected final CommonStepProperties properties;
/**
* Create a new {@link StepBuilderHelper}.
* @param name the step name
* @deprecated use {@link StepBuilderHelper#StepBuilderHelper(String, JobRepository)}
*/
@Deprecated(since = "5.1", forRemoval = true)
public StepBuilderHelper(String name) {
this.properties = new CommonStepProperties();
properties.name = name;
}
/**
* Create a new {@link StepBuilderHelper}.
* @param name the step name
@@ -85,18 +74,6 @@ public abstract class StepBuilderHelper<B extends StepBuilderHelper<B>> {
this.properties = new CommonStepProperties(parent.properties);
}
/**
* Set the job repository
* @param jobRepository the repository to set
* @return this to enable fluent chaining
* @deprecated use {@link StepBuilderHelper#StepBuilderHelper(String, JobRepository)}
*/
@Deprecated(since = "5.1", forRemoval = true)
public B repository(JobRepository jobRepository) {
properties.jobRepository = jobRepository;
return self();
}
/**
* Sets the step observation convention.
* @param observationConvention the step observation convention (optional)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,18 +38,6 @@ public class TaskletStepBuilder extends AbstractTaskletStepBuilder<TaskletStepBu
super(parent);
}
/**
* @param tasklet the tasklet to use
* @return this for fluent chaining
* @deprecated use
* {@link TaskletStepBuilder#tasklet(Tasklet, PlatformTransactionManager)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public TaskletStepBuilder tasklet(Tasklet tasklet) {
this.tasklet = tasklet;
return this;
}
/**
* @param tasklet the tasklet to use
* @return this for fluent chaining

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -441,20 +441,6 @@ public class SimpleStepFactoryBean<T, S> implements FactoryBean<Step>, BeanNameA
return taskExecutor;
}
/**
* Public setter for the throttle limit. This limits the number of tasks queued for
* concurrent processing to prevent thread pools from being overwhelmed. Defaults to
* {@link TaskExecutorRepeatTemplate#DEFAULT_THROTTLE_LIMIT}.
* @param throttleLimit the throttle limit to set.
* @deprecated since 5.0, scheduled for removal in 6.0. Use a pooled
* {@link TaskExecutor} implementation with a limited capacity of its task queue
* instead.
*/
@Deprecated(since = "5.0", forRemoval = true)
public void setThrottleLimit(int throttleLimit) {
this.throttleLimit = throttleLimit;
}
protected void applyConfiguration(SimpleStepBuilder<T, S> builder) {
builder.reader(itemReader);
@@ -482,7 +468,6 @@ public class SimpleStepFactoryBean<T, S> implements FactoryBean<Step>, BeanNameA
}
builder.transactionManager(transactionManager);
builder.transactionAttribute(getTransactionAttribute());
builder.repository(jobRepository);
builder.observationRegistry(observationRegistry);
builder.startLimit(startLimit);
builder.allowStartIfComplete(allowStartIfComplete);
@@ -491,7 +476,6 @@ public class SimpleStepFactoryBean<T, S> implements FactoryBean<Step>, BeanNameA
builder.chunkOperations(chunkOperations);
builder.stepOperations(stepOperations);
builder.taskExecutor(taskExecutor);
builder.throttleLimit(throttleLimit);
builder.exceptionHandler(exceptionHandler);
if (isReaderTransactionalQueue) {
builder.readerIsTransactionalQueue();

View File

@@ -6,7 +6,7 @@
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tool https://www.springframework.org/schema/tool/spring-tool.xsd"
version="5.0">
version="6.0">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
@@ -265,20 +265,6 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="lob-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the lob handler (optional). Only override if using Oracle and
the database type is not being detected for some reason.
This attribute is deprecated Since 5.2 with no replacement. Scheduled for removal in v6.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jdbc.support.lob.LobHandler" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="conversion-service" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -680,16 +666,6 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="throttle-limit" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
This limits the number of tasks queued for concurrent
processing to prevent thread pools from being overwhelmed.
Default is 4. This is deprecated in favor of using similar
features in TaskExecutor implementations.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="allowStartIfCompleteAttribute"/>
</xsd:complexType>

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.configuration.support;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Collection;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.job.JobSupport;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Dave Syer
* @author Mahmoud Ben Hassine
*
*/
class JobRegistryBeanPostProcessorTests {
private final JobRegistryBeanPostProcessor processor = new JobRegistryBeanPostProcessor();
@Test
void testInitializationFails() {
Exception exception = assertThrows(IllegalStateException.class, processor::afterPropertiesSet);
assertTrue(exception.getMessage().contains("JobRegistry"));
}
@Test
void testBeforeInitialization() {
// should be a no-op
assertEquals("foo", processor.postProcessBeforeInitialization("foo", "bar"));
}
@Test
void testAfterInitializationWithWrongType() {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo", "bar"));
}
@Test
void testAfterInitializationWithCorrectType() {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertNotNull(processor.postProcessAfterInitialization(job, "bar"));
assertEquals("[foo]", registry.getJobNames().toString());
}
@Test
void testAfterInitializationWithGroupName() {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
processor.setGroupName("jobs");
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertNotNull(processor.postProcessAfterInitialization(job, "bar"));
assertEquals("[jobs.foo]", registry.getJobNames().toString());
}
@Test
void testAfterInitializationWithDuplicate() {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport job = new JobSupport();
job.setBeanName("foo");
processor.postProcessAfterInitialization(job, "bar");
Exception exception = assertThrows(FatalBeanException.class,
() -> processor.postProcessAfterInitialization(job, "spam"));
assertTrue(exception.getCause() instanceof DuplicateJobException);
}
@Test
void testUnregisterOnDestroy() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport job = new JobSupport();
job.setBeanName("foo");
assertNotNull(processor.postProcessAfterInitialization(job, "bar"));
processor.destroy();
assertEquals("[]", registry.getJobNames().toString());
}
@Test
void testExecutionWithApplicationContext() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test-context.xml", getClass());
MapJobRegistry registry = (MapJobRegistry) context.getBean("registry");
Collection<String> configurations = registry.getJobNames();
String[] names = context.getBeanNamesForType(JobSupport.class);
int count = names.length;
// Each concrete bean of type JobConfiguration is registered...
assertEquals(count, configurations.size());
// N.B. there is a failure / wonky mode where a parent bean is given an
// explicit name or beanName (using property setter): in this case then
// child beans will have the same name and will be re-registered (and
// override, if the registry supports that).
assertNotNull(registry.getJob("test-job"));
assertEquals(context.getBean("test-job-with-name"), registry.getJob("foo"));
assertEquals(context.getBean("test-job-with-bean-name"), registry.getJob("bar"));
assertEquals(context.getBean("test-job-with-parent-and-name"), registry.getJob("spam"));
assertEquals(context.getBean("test-job-with-parent-and-bean-name"), registry.getJob("bucket"));
assertEquals(context.getBean("test-job-with-concrete-parent"), registry.getJob("maps"));
assertEquals(context.getBean("test-job-with-concrete-parent-and-name"), registry.getJob("oof"));
assertEquals(context.getBean("test-job-with-concrete-parent-and-bean-name"), registry.getJob("rab"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -239,7 +239,6 @@ class StepParserStepFactoryBeanTests {
fb.setIsReaderTransactionalQueue(true);
fb.setRetryLimit(5);
fb.setSkipLimit(100);
fb.setThrottleLimit(10);
fb.setRetryListeners(new RetryListener() {
});
@SuppressWarnings("unchecked")
@@ -251,7 +250,7 @@ class StepParserStepFactoryBeanTests {
assertTrue(step instanceof TaskletStep);
Object throttleLimit = ReflectionTestUtils.getField(ReflectionTestUtils.getField(step, "stepOperations"),
"throttleLimit");
assertEquals(10, throttleLimit);
assertEquals(4, throttleLimit);
Object tasklet = ReflectionTestUtils.getField(step, "tasklet");
assertTrue(tasklet instanceof ChunkOrientedTasklet<?>);
assertFalse((Boolean) ReflectionTestUtils.getField(tasklet, "buffering"));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -88,8 +88,6 @@ public class StepParserTests {
StepParserStepFactoryBean<Object, Object> factory = beans.get(factoryName);
TaskletStep bean = (TaskletStep) factory.getObject();
assertEquals(25, bean.getStartLimit(), "wrong start-limit:");
Object throttleLimit = ReflectionTestUtils.getField(factory, "throttleLimit");
assertEquals(10, throttleLimit);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,8 +42,6 @@ import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
@@ -112,48 +110,6 @@ class JobRepositoryFactoryBeanTests {
}
@Test
void testOracleLobHandler() throws Exception {
factory.setDatabaseType("ORACLE");
incrementerFactory = mock();
when(incrementerFactory.isSupportedIncrementerType("ORACLE")).thenReturn(true);
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).thenReturn(new StubIncrementer());
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ"))
.thenReturn(new StubIncrementer());
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ"))
.thenReturn(new StubIncrementer());
factory.setIncrementerFactory(incrementerFactory);
factory.afterPropertiesSet();
LobHandler lobHandler = (LobHandler) ReflectionTestUtils.getField(factory, "lobHandler");
assertTrue(lobHandler instanceof DefaultLobHandler);
}
@Test
void testCustomLobHandler() throws Exception {
factory.setDatabaseType("ORACLE");
incrementerFactory = mock();
when(incrementerFactory.isSupportedIncrementerType("ORACLE")).thenReturn(true);
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_SEQ")).thenReturn(new StubIncrementer());
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "JOB_EXECUTION_SEQ"))
.thenReturn(new StubIncrementer());
when(incrementerFactory.getIncrementer("ORACLE", tablePrefix + "STEP_EXECUTION_SEQ"))
.thenReturn(new StubIncrementer());
factory.setIncrementerFactory(incrementerFactory);
LobHandler lobHandler = new DefaultLobHandler();
factory.setLobHandler(lobHandler);
factory.afterPropertiesSet();
assertEquals(lobHandler, ReflectionTestUtils.getField(factory, "lobHandler"));
}
@Test
@SuppressWarnings("unchecked")
void tesDefaultSerializer() throws Exception {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -252,11 +252,6 @@ class StepBuilderTests {
testReturnedTypeOfSetterIsAssignableToSimpleStepBuilder(builder -> builder.taskExecutor(null));
}
@Test
void testReturnedTypeOfThrottleLimitIsAssignableToSimpleStepBuilder() throws Exception {
testReturnedTypeOfSetterIsAssignableToSimpleStepBuilder(builder -> builder.throttleLimit(4));
}
@Test
void testReturnedTypeOfExceptionHandlerIsAssignableToSimpleStepBuilder() throws Exception {
testReturnedTypeOfSetterIsAssignableToSimpleStepBuilder(

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2024 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -151,7 +151,6 @@ class SimpleStepFactoryBeanTests {
SimpleStepFactoryBean<String, String> factory = getStepFactory("foo", "bar");
factory.setTaskExecutor(new SimpleAsyncTaskExecutor());
factory.setThrottleLimit(1);
AbstractStep step = (AbstractStep) factory.getObject();
step.setName("step1");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -109,7 +109,6 @@ class AsyncChunkOrientedStepIntegrationTests {
job = new JobSupport("FOO");
TaskExecutorRepeatTemplate repeatTemplate = new TaskExecutorRepeatTemplate();
repeatTemplate.setThrottleLimit(2);
repeatTemplate.setTaskExecutor(new SimpleAsyncTaskExecutor());
step.setStepOperations(repeatTemplate);
step.setTransactionManager(transactionManager);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,7 +90,6 @@ class AsyncTaskletStepTests {
step.setJobRepository(jobRepository);
TaskExecutorRepeatTemplate template = new TaskExecutorRepeatTemplate();
template.setThrottleLimit(throttleLimit);
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.setConcurrencyLimit(concurrencyLimit);
template.setTaskExecutor(taskExecutor);

View File

@@ -13,7 +13,7 @@
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry"/>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>

View File

@@ -10,7 +10,7 @@
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
<bean
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="registry" />
</bean>

View File

@@ -11,7 +11,7 @@
</property>
</bean>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry" />
</bean>

View File

@@ -20,7 +20,7 @@
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry" />
</bean>

View File

@@ -14,9 +14,8 @@
<beans:bean id="transactionManager" class="org.springframework.jdbc.support.JdbcTransactionManager">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<beans:bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>
<beans:bean id="serializer" class="org.springframework.batch.core.repository.dao.DefaultExecutionContextSerializer"/>
<job-repository id="jobRepo1" data-source="dataSource" jdbc-operations="jdbcTemplate" transaction-manager="transactionManager" lob-handler="lobHandler" max-varchar-length="100" serializer="serializer"/>
<job-repository id="jobRepo1" data-source="dataSource" jdbc-operations="jdbcTemplate" transaction-manager="transactionManager" max-varchar-length="100" serializer="serializer"/>
</beans:beans>

View File

@@ -9,7 +9,7 @@
<job id="job">
<step id="step">
<tasklet start-limit="25" allow-start-if-complete="true" task-executor="taskExecutor" throttle-limit="10">
<tasklet start-limit="25" allow-start-if-complete="true" task-executor="taskExecutor">
<chunk reader="reader" processor="processor" writer="writer" commit-interval="10" skip-limit="20"
retry-limit="3" cache-capacity="100" reader-transactional-queue="true">
<retry-listeners>

View File

@@ -38,7 +38,7 @@
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry"/>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>

View File

@@ -15,7 +15,7 @@
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry"/>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>

View File

@@ -9,7 +9,7 @@
<import resource="test-environment-with-registry.xml" />
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>

View File

@@ -11,7 +11,7 @@
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>

View File

@@ -52,11 +52,10 @@ This reader stores message offsets in the execution context to support restart c
rows, such that large datasets can be read without running out of
memory.|Yes
|`ListItemReader`|Provides the items from a list, one at a time.|No
|`MongoItemReader`|Given a `MongoOperations` object and a JSON-based MongoDB
|`MongoPagingItemReader`|Given a `MongoOperations` object and a JSON-based MongoDB
query, provides items received from the `MongoOperations#find()` method.|Yes
|`Neo4jItemReader`|Given a `Neo4jOperations` object and the components of a
Cyhper query, items are returned as the result of the Neo4jOperations.query
method.|Yes
|`MongoCursorItemReader`|Given a `MongoOperations` object and a JSON-based MongoDB
query, provides items received from the `MongoOperations#stream()` method.|Yes
|`RepositoryItemReader`|Given a Spring Data `PagingAndSortingRepository` object,
a `Sort`, and the name of method to execute, returns items provided by the
Spring Data repository implementation.|Yes
@@ -106,9 +105,6 @@ This reader stores message offsets in the execution context to support restart c
|`MongoItemWriter`|Given a `MongoOperations` object, items are written
through the `MongoOperations.save(Object)` method. The actual write is delayed
until the last possible moment before the transaction commits.|Yes
|`Neo4jItemWriter`|Given a `Neo4jOperations` object, items are persisted through the
`save(Object)` method or deleted through the `delete(Object)`, as dictated by the
`ItemWriter's` configuration|Yes
|`PropertyExtractingDelegatingItemWriter`|Extends `AbstractMethodInvokingDelegator`
creating arguments on the fly. Arguments are created by retrieving
the values from the fields in the item to be processed (through a

View File

@@ -177,58 +177,6 @@ You can populate a `JobRegistry` in one of the following ways: by using
a bean post processor, or by using a smart initializing singleton or by using
a registrar lifecycle component. The coming sections describe these mechanisms.
[[jobregistrybeanpostprocessor]]
=== JobRegistryBeanPostProcessor
This is a bean post-processor that can register all jobs as they are created.
[tabs]
====
Java::
+
The following example shows how to include the `JobRegistryBeanPostProcessor` for a job
defined in Java:
+
.Java Configuration
[source, java]
----
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();
postProcessor.setJobRegistry(jobRegistry);
return postProcessor;
}
----
XML::
+
The following example shows how to include the `JobRegistryBeanPostProcessor` for a job
defined in XML:
+
.XML Configuration
[source, xml]
----
<bean id="jobRegistryBeanPostProcessor" class="org.spr...JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
----
====
Although it is not strictly necessary, the post-processor in the
example has been given an `id` so that it can be included in child
contexts (for example, as a parent bean definition) and cause all jobs created
there to also be registered automatically.
[WARNING]
.Deprecation
====
As of version 5.2, the `JobRegistryBeanPostProcessor` class is deprecated in favor of
`JobRegistrySmartInitializingSingleton`, see xref:#jobregistrysmartinitializingsingleton[JobRegistrySmartInitializingSingleton].
====
[[jobregistrysmartinitializingsingleton]]
=== JobRegistrySmartInitializingSingleton
@@ -343,7 +291,7 @@ configuration in the child, provided it should be the same as the
parent.
You can use `AutomaticJobRegistrar` in
conjunction with a `JobRegistryBeanPostProcessor`
conjunction with a `JobRegistrySmartInitializingSingleton`
(as long as you also use `DefaultJobLoader`).
For instance, this might be desirable if there are jobs
defined in the main parent context as well as in the child

View File

@@ -161,21 +161,21 @@ construct an instance of the `KafkaItemWriter`.
== Database Readers
Spring Batch offers the following database readers:
* xref:readers-and-writers/item-reader-writer-implementations.adoc#Neo4jItemReader[`Neo4jItemReader`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#mongoItemReader[`MongoItemReader`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#mongoPagingItemReader[`MongoPagingItemReader`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#mongoCursorItemReader[`MongoCursorItemReader`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#repositoryItemReader[`RepositoryItemReader`]
[[Neo4jItemReader]]
=== `Neo4jItemReader`
The `Neo4jItemReader` is an `ItemReader` that reads objects from the graph database Neo4j
by using a paging technique. Spring Batch provides a `Neo4jItemReaderBuilder` to
construct an instance of the `Neo4jItemReader`.
[[mongoPagingItemReader]]
=== `MongoPagingItemReader`
The `MongoPagingItemReader` is an `ItemReader` that reads documents from MongoDB by using a
paging technique. Spring Batch provides a `MongoPagingItemReaderBuilder` to construct an
instance of the `MongoPagingItemReader`.
[[mongoItemReader]]
=== `MongoItemReader`
The `MongoItemReader` is an `ItemReader` that reads documents from MongoDB by using a
paging technique. Spring Batch provides a `MongoItemReaderBuilder` to construct an
instance of the `MongoItemReader`.
[[mongoCursorItemReader]]
=== `MongoCursorItemReader`
The `MongoCursorItemReader` is an `ItemReader` that reads documents from MongoDB by using a
streaming technique. Spring Batch provides a `MongoCursorItemReaderBuilder` to construct an
instance of the `MongoCursorItemReader`.
[[repositoryItemReader]]
=== `RepositoryItemReader`
@@ -187,18 +187,11 @@ construct an instance of the `RepositoryItemReader`.
== Database Writers
Spring Batch offers the following database writers:
* xref:readers-and-writers/item-reader-writer-implementations.adoc#neo4jItemWriter[`Neo4jItemWriter`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#mongoItemWriter[`MongoItemWriter`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#repositoryItemWriter[`RepositoryItemWriter`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#jdbcBatchItemWriter[`JdbcBatchItemWriter`]
* xref:readers-and-writers/item-reader-writer-implementations.adoc#jpaItemWriter[`JpaItemWriter`]
[[neo4jItemWriter]]
=== `Neo4jItemWriter`
The `Neo4jItemWriter` is an `ItemWriter` implementation that writes to a Neo4j database.
Spring Batch provides a `Neo4jItemWriterBuilder` to construct an instance of the
`Neo4jItemWriter`.
[[mongoItemWriter]]
=== `MongoItemWriter`
The `MongoItemWriter` is an `ItemWriter` implementation that writes to a MongoDB store

View File

@@ -133,29 +133,6 @@ Note also that there may be limits placed on concurrency by any pooled resources
your step, such as a `DataSource`. Be sure to make the pool in those resources at least
as large as the desired number of concurrent threads in the step.
[WARNING]
.Throttle limit deprecation
====
As of v5.0, the throttle limit is deprecated with no replacement. If you want to replace the
current throttling mechanism in the default `TaskExecutorRepeatTemplate`, you need to provide
a custom `RepeatOperations` implementation (based on a `TaskExecutor` with a bounded task queue)
and set it on the step with `StepBuilder#stepOperations`:
.Java Configuration
[source, java]
----
@Bean
public Step sampleStep(RepeatOperations customRepeatOperations, JobRepository jobRepository, PlatformTransactionManager transactionManager) {
return new StepBuilder("sampleStep", jobRepository)
.<String, String>chunk(10, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.stepOperations(customRepeatOperations)
.build();
}
----
====
There are some practical limitations of using multi-threaded `Step` implementations for
some common batch use cases. Many participants in a `Step` (such as readers and writers)
are stateful. If the state is not segregated by thread, those components are not

View File

@@ -1,266 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.bson.codecs.DecoderContext;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.mongodb.util.json.ParameterBindingJsonReader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* <p>
* Restartable {@link ItemReader} that reads documents from MongoDB via a paging
* technique.
* </p>
*
* <p>
* If you set JSON String query {@link #setQuery(String)} then it executes the JSON to
* retrieve the requested documents.
* </p>
*
* <p>
* If you set Query object {@link #setQuery(Query)} then it executes the Query to retrieve
* the requested documents.
* </p>
*
* <p>
* The query is executed using paged requests specified in the {@link #setPageSize(int)}.
* Additional pages are requested as needed to provide data when the {@link #read()}
* method is called.
* </p>
*
* <p>
* The JSON String query provided supports parameter substitution via ?&lt;index&gt;
* placeholders where the &lt;index&gt; indicates the index of the parameterValue to
* substitute.
* </p>
*
* <p>
* The implementation is thread-safe between calls to {@link #open(ExecutionContext)}, but
* remember to use <code>saveState=false</code> if used in a multi-threaded client (no
* restart available).
* </p>
*
* @author Michael Minella
* @author Takaaki Iida
* @author Mahmoud Ben Hassine
* @author Parikshit Dutta
* @deprecated Use {@link MongoPagingItemReader} instead. Scheduled for removal in v5.3 or
* later.
*/
@Deprecated(since = "5.1", forRemoval = true)
public class MongoItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
protected MongoOperations template;
protected Query query;
protected String queryString;
protected Class<? extends T> type;
protected Sort sort;
protected String hint;
protected String fields;
protected String collection;
protected List<Object> parameterValues = new ArrayList<>();
public MongoItemReader() {
super();
setName(ClassUtils.getShortName(MongoItemReader.class));
}
/**
* A Mongo Query to be used.
* @param query Mongo Query to be used.
*/
public void setQuery(Query query) {
this.query = query;
}
/**
* Used to perform operations against the MongoDB instance. Also handles the mapping
* of documents to objects.
* @param template the MongoOperations instance to use
* @see MongoOperations
*/
public void setTemplate(MongoOperations template) {
this.template = template;
}
/**
* A JSON formatted MongoDB query. Parameterization of the provided query is allowed
* via ?&lt;index&gt; placeholders where the &lt;index&gt; indicates the index of the
* parameterValue to substitute.
* @param queryString JSON formatted Mongo query
*/
public void setQuery(String queryString) {
this.queryString = queryString;
}
/**
* The type of object to be returned for each {@link #read()} call.
* @param type the type of object to return
*/
public void setTargetType(Class<? extends T> type) {
this.type = type;
}
/**
* {@link List} of values to be substituted in for each of the parameters in the
* query.
* @param parameterValues values
*/
public void setParameterValues(List<Object> parameterValues) {
Assert.notNull(parameterValues, "Parameter values must not be null");
this.parameterValues = parameterValues;
}
/**
* JSON defining the fields to be returned from the matching documents by MongoDB.
* @param fields JSON string that identifies the fields to sort by.
*/
public void setFields(String fields) {
this.fields = fields;
}
/**
* {@link Map} of property
* names/{@link org.springframework.data.domain.Sort.Direction} values to sort the
* input by.
* @param sorts map of properties and direction to sort each.
*/
public void setSort(Map<String, Sort.Direction> sorts) {
Assert.notNull(sorts, "Sorts must not be null");
this.sort = convertToSort(sorts);
}
/**
* @param collection Mongo collection to be queried.
*/
public void setCollection(String collection) {
this.collection = collection;
}
/**
* JSON String telling MongoDB what index to use.
* @param hint string indicating what index to use.
*/
public void setHint(String hint) {
this.hint = hint;
}
@Override
@SuppressWarnings("unchecked")
protected Iterator<T> doPageRead() {
if (queryString != null) {
Pageable pageRequest = PageRequest.of(page, pageSize, sort);
String populatedQuery = replacePlaceholders(queryString, parameterValues);
Query mongoQuery;
if (StringUtils.hasText(fields)) {
mongoQuery = new BasicQuery(populatedQuery, fields);
}
else {
mongoQuery = new BasicQuery(populatedQuery);
}
mongoQuery.with(pageRequest);
if (StringUtils.hasText(hint)) {
mongoQuery.withHint(hint);
}
if (StringUtils.hasText(collection)) {
return (Iterator<T>) template.find(mongoQuery, type, collection).iterator();
}
else {
return (Iterator<T>) template.find(mongoQuery, type).iterator();
}
}
else {
Pageable pageRequest = PageRequest.of(page, pageSize);
query.with(pageRequest);
if (StringUtils.hasText(collection)) {
return (Iterator<T>) template.find(query, type, collection).iterator();
}
else {
return (Iterator<T>) template.find(query, type).iterator();
}
}
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(template != null, "An implementation of MongoOperations is required.");
Assert.state(type != null, "A type to convert the input into is required.");
Assert.state(queryString != null || query != null, "A query is required.");
if (queryString != null) {
Assert.state(sort != null, "A sort is required.");
}
}
protected String replacePlaceholders(String input, List<Object> values) {
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(input, values.toArray());
DecoderContext decoderContext = DecoderContext.builder().build();
Document document = new ParameterBindingDocumentCodec().decode(reader, decoderContext);
return document.toJson();
}
protected Sort convertToSort(Map<String, Sort.Direction> sorts) {
List<Sort.Order> sortValues = new ArrayList<>(sorts.size());
for (Map.Entry<String, Sort.Direction> curSort : sorts.entrySet()) {
sortValues.add(new Sort.Order(curSort.getValue(), curSort.getKey()));
}
return Sort.by(sortValues);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,19 +97,6 @@ public class MongoItemWriter<T> implements ItemWriter<T>, InitializingBean {
this.bufferKey = new Object();
}
/**
* Indicates if the items being passed to the writer are to be saved or removed from
* the data store. If set to false (default), the items will be saved or update using
* {@link Mode#UPSERT}. If set to true, then items will be removed.
* @param delete removal indicator
* @deprecated use {@link MongoItemWriter#setMode(Mode)} instead. Scheduled for
* removal in v5.3 or later.
*/
@Deprecated(since = "5.1", forRemoval = true)
public void setDelete(boolean delete) {
this.mode = (delete) ? Mode.REMOVE : Mode.UPSERT;
}
/**
* Set the operating {@link Mode} to be applied by this writer. Defaults to
* {@link Mode#UPSERT}.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,27 @@
*/
package org.springframework.batch.item.data;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.bson.codecs.DecoderContext;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.mongodb.util.json.ParameterBindingJsonReader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* <p>
@@ -67,78 +78,186 @@ import org.springframework.util.ClassUtils;
* @author Mahmoud Ben Hassine
* @author Parikshit Dutta
*/
public class MongoPagingItemReader<T> extends MongoItemReader<T> {
public class MongoPagingItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
protected MongoOperations template;
protected Query query;
protected String queryString;
protected Class<? extends T> type;
protected Sort sort;
protected String hint;
protected String fields;
protected String collection;
protected List<Object> parameterValues = new ArrayList<>();
/**
* Create a new {@link MongoPagingItemReader}.
*/
public MongoPagingItemReader() {
super();
setName(ClassUtils.getShortName(MongoPagingItemReader.class));
}
@Override
public void setTemplate(MongoOperations template) {
super.setTemplate(template);
}
@Override
/**
* A Mongo Query to be used.
* @param query Mongo Query to be used.
*/
public void setQuery(Query query) {
super.setQuery(query);
this.query = query;
}
@Override
/**
* Used to perform operations against the MongoDB instance. Also handles the mapping
* of documents to objects.
* @param template the MongoOperations instance to use
* @see MongoOperations
*/
public void setTemplate(MongoOperations template) {
this.template = template;
}
/**
* A JSON formatted MongoDB query. Parameterization of the provided query is allowed
* via ?&lt;index&gt; placeholders where the &lt;index&gt; indicates the index of the
* parameterValue to substitute.
* @param queryString JSON formatted Mongo query
*/
public void setQuery(String queryString) {
super.setQuery(queryString);
this.queryString = queryString;
}
@Override
/**
* The type of object to be returned for each {@link #read()} call.
* @param type the type of object to return
*/
public void setTargetType(Class<? extends T> type) {
super.setTargetType(type);
this.type = type;
}
@Override
/**
* {@link List} of values to be substituted in for each of the parameters in the
* query.
* @param parameterValues values
*/
public void setParameterValues(List<Object> parameterValues) {
super.setParameterValues(parameterValues);
Assert.notNull(parameterValues, "Parameter values must not be null");
this.parameterValues = parameterValues;
}
@Override
/**
* JSON defining the fields to be returned from the matching documents by MongoDB.
* @param fields JSON string that identifies the fields to sort by.
*/
public void setFields(String fields) {
super.setFields(fields);
this.fields = fields;
}
@Override
/**
* {@link Map} of property
* names/{@link org.springframework.data.domain.Sort.Direction} values to sort the
* input by.
* @param sorts map of properties and direction to sort each.
*/
public void setSort(Map<String, Sort.Direction> sorts) {
super.setSort(sorts);
Assert.notNull(sorts, "Sorts must not be null");
this.sort = convertToSort(sorts);
}
@Override
/**
* @param collection Mongo collection to be queried.
*/
public void setCollection(String collection) {
super.setCollection(collection);
this.collection = collection;
}
/**
* JSON String telling MongoDB what index to use.
* @param hint string indicating what index to use.
*/
public void setHint(String hint) {
this.hint = hint;
}
@Override
public void setHint(String hint) {
super.setHint(hint);
@SuppressWarnings("unchecked")
protected Iterator<T> doPageRead() {
if (queryString != null) {
Pageable pageRequest = PageRequest.of(page, pageSize, sort);
String populatedQuery = replacePlaceholders(queryString, parameterValues);
Query mongoQuery;
if (StringUtils.hasText(fields)) {
mongoQuery = new BasicQuery(populatedQuery, fields);
}
else {
mongoQuery = new BasicQuery(populatedQuery);
}
mongoQuery.with(pageRequest);
if (StringUtils.hasText(hint)) {
mongoQuery.withHint(hint);
}
if (StringUtils.hasText(collection)) {
return (Iterator<T>) template.find(mongoQuery, type, collection).iterator();
}
else {
return (Iterator<T>) template.find(mongoQuery, type).iterator();
}
}
else {
Pageable pageRequest = PageRequest.of(page, pageSize);
query.with(pageRequest);
if (StringUtils.hasText(collection)) {
return (Iterator<T>) template.find(query, type, collection).iterator();
}
else {
return (Iterator<T>) template.find(query, type).iterator();
}
}
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.state(template != null, "An implementation of MongoOperations is required.");
Assert.state(type != null, "A type to convert the input into is required.");
Assert.state(queryString != null || query != null, "A query is required.");
if (queryString != null) {
Assert.state(sort != null, "A sort is required.");
}
}
@Override
protected Iterator<T> doPageRead() {
return super.doPageRead();
}
@Override
protected String replacePlaceholders(String input, List<Object> values) {
return super.replacePlaceholders(input, values);
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(input, values.toArray());
DecoderContext decoderContext = DecoderContext.builder().build();
Document document = new ParameterBindingDocumentCodec().decode(reader, decoderContext);
return document.toJson();
}
@Override
protected Sort convertToSort(Map<String, Sort.Direction> sorts) {
return super.convertToSort(sorts);
List<Sort.Order> sortValues = new ArrayList<>(sorts.size());
for (Map.Entry<String, Sort.Direction> curSort : sorts.entrySet()) {
sortValues.add(new Sort.Order(curSort.getValue(), curSort.getKey()));
}
return Sort.by(sortValues);
}
}
}

View File

@@ -1,217 +0,0 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>
* Restartable {@link ItemReader} that reads objects from the graph database Neo4j via a
* paging technique.
* </p>
*
* <p>
* It executes cypher queries built from the statement fragments provided to retrieve the
* requested data. The query is executed using paged requests of a size specified in
* {@link #setPageSize(int)}. Additional pages are requested as needed when the
* {@link #read()} method is called. On restart, the reader will begin again at the same
* number item it left off at.
* </p>
*
* <p>
* Performance is dependent on your Neo4J configuration (embedded or remote) as well as
* page size. Setting a fairly large page size and using a commit interval that matches
* the page size should provide better performance.
* </p>
*
* <p>
* This implementation is thread-safe between calls to
* {@link #open(org.springframework.batch.item.ExecutionContext)}, however you should set
* <code>saveState=false</code> if used in a multi-threaded environment (no restart
* available).
* </p>
*
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @deprecated since 5.0 in favor of the item reader from <a href=
* "https://github.com/spring-projects/spring-batch-extensions/blob/main/spring-batch-neo4j">...</a>
*/
@Deprecated
public class Neo4jItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
protected Log logger = LogFactory.getLog(getClass());
private SessionFactory sessionFactory;
private String startStatement;
private String returnStatement;
private String matchStatement;
private String whereStatement;
private String orderByStatement;
private Class<T> targetType;
private Map<String, Object> parameterValues;
/**
* Optional parameters to be used in the cypher query.
* @param parameterValues the parameter values to be used in the cypher query
*/
public void setParameterValues(Map<String, Object> parameterValues) {
this.parameterValues = parameterValues;
}
protected final Map<String, Object> getParameterValues() {
return this.parameterValues;
}
/**
* The start segment of the cypher query. START is prepended to the statement provided
* and should <em>not</em> be included.
* @param startStatement the start fragment of the cypher query.
*/
public void setStartStatement(String startStatement) {
this.startStatement = startStatement;
}
/**
* The return statement of the cypher query. RETURN is prepended to the statement
* provided and should <em>not</em> be included
* @param returnStatement the return fragment of the cypher query.
*/
public void setReturnStatement(String returnStatement) {
this.returnStatement = returnStatement;
}
/**
* An optional match fragment of the cypher query. MATCH is prepended to the statement
* provided and should <em>not</em> be included.
* @param matchStatement the match fragment of the cypher query
*/
public void setMatchStatement(String matchStatement) {
this.matchStatement = matchStatement;
}
/**
* An optional where fragment of the cypher query. WHERE is prepended to the statement
* provided and should <em>not</em> be included.
* @param whereStatement where fragment of the cypher query
*/
public void setWhereStatement(String whereStatement) {
this.whereStatement = whereStatement;
}
/**
* A list of properties to order the results by. This is required so that subsequent
* page requests pull back the segment of results correctly. ORDER BY is prepended to
* the statement provided and should <em>not</em> be included.
* @param orderByStatement order by fragment of the cypher query.
*/
public void setOrderByStatement(String orderByStatement) {
this.orderByStatement = orderByStatement;
}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* Establish the session factory for the reader.
* @param sessionFactory the factory to use for the reader.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* The object type to be returned from each call to {@link #read()}
* @param targetType the type of object to return.
*/
public void setTargetType(Class<T> targetType) {
this.targetType = targetType;
}
protected final Class<T> getTargetType() {
return this.targetType;
}
protected String generateLimitCypherQuery() {
StringBuilder query = new StringBuilder(128);
query.append("START ").append(startStatement);
query.append(matchStatement != null ? " MATCH " + matchStatement : "");
query.append(whereStatement != null ? " WHERE " + whereStatement : "");
query.append(" RETURN ").append(returnStatement);
query.append(" ORDER BY ").append(orderByStatement);
query.append(" SKIP ").append(pageSize * page);
query.append(" LIMIT ").append(pageSize);
String resultingQuery = query.toString();
if (logger.isDebugEnabled()) {
logger.debug(resultingQuery);
}
return resultingQuery;
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(sessionFactory != null, "A SessionFactory is required");
Assert.state(targetType != null, "The type to be returned is required");
Assert.state(StringUtils.hasText(startStatement), "A START statement is required");
Assert.state(StringUtils.hasText(returnStatement), "A RETURN statement is required");
Assert.state(StringUtils.hasText(orderByStatement), "A ORDER BY statement is required");
}
@SuppressWarnings("unchecked")
@Override
protected Iterator<T> doPageRead() {
Session session = getSessionFactory().openSession();
Iterable<T> queryResults = session.query(getTargetType(), generateLimitCypherQuery(), getParameterValues());
if (queryResults != null) {
return queryResults.iterator();
}
else {
return new ArrayList<T>().iterator();
}
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* <p>
* A {@link ItemWriter} implementation that writes to a Neo4j database.
* </p>
*
* <p>
* This writer is thread-safe once all properties are set (normal singleton behavior) so
* it can be used in multiple concurrent transactions.
* </p>
*
* @author Michael Minella
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
* @deprecated since 5.0 in favor of the item writer from <a href=
* "https://github.com/spring-projects/spring-batch-extensions/blob/main/spring-batch-neo4j">...</a>
*
*/
@Deprecated
public class Neo4jItemWriter<T> implements ItemWriter<T>, InitializingBean {
protected static final Log logger = LogFactory.getLog(Neo4jItemWriter.class);
private boolean delete = false;
private SessionFactory sessionFactory;
/**
* Boolean flag indicating whether the writer should save or delete the item at write
* time.
* @param delete true if write should delete item, false if item should be saved.
* Default is false.
*/
public void setDelete(boolean delete) {
this.delete = delete;
}
/**
* Establish the session factory that will be used to create {@link Session} instances
* for interacting with Neo4j.
* @param sessionFactory sessionFactory to be used.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Checks mandatory properties
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(this.sessionFactory != null, "A SessionFactory is required");
}
/**
* Write all items to the data store.
*
* @see org.springframework.batch.item.ItemWriter#write(Chunk)
*/
@Override
public void write(Chunk<? extends T> chunk) throws Exception {
if (!chunk.isEmpty()) {
doWrite(chunk);
}
}
/**
* Performs the actual write using the template. This can be overridden by a subclass
* if necessary.
* @param items the list of items to be persisted.
*/
protected void doWrite(Chunk<? extends T> items) {
if (delete) {
delete(items);
}
else {
save(items);
}
}
private void delete(Chunk<? extends T> items) {
Session session = this.sessionFactory.openSession();
for (T item : items) {
session.delete(item);
}
}
private void save(Chunk<? extends T> items) {
Session session = this.sessionFactory.openSession();
for (T item : items) {
session.save(item);
}
}
}

View File

@@ -1,299 +0,0 @@
/*
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.batch.item.data.MongoItemReader;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A builder implementation for the {@link MongoItemReader}
*
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
* @author Drummond Dawson
* @author Parikshit Dutta
* @since 4.0
* @see MongoItemReader
* @deprecated Use {@link MongoPagingItemReaderBuilder} instead. Scheduled for removal in
* v5.3 or later.
*/
@Deprecated(since = "5.1", forRemoval = true)
public class MongoItemReaderBuilder<T> {
protected MongoOperations template;
protected String jsonQuery;
protected Class<? extends T> targetType;
protected Map<String, Sort.Direction> sorts;
protected String hint;
protected String fields;
protected String collection;
protected List<Object> parameterValues = new ArrayList<>();
protected int pageSize = 10;
protected boolean saveState = true;
protected String name;
protected int maxItemCount = Integer.MAX_VALUE;
protected int currentItemCount;
protected Query query;
/**
* Configure if the state of the
* {@link org.springframework.batch.item.ItemStreamSupport} should be persisted within
* the {@link org.springframework.batch.item.ExecutionContext} for restart purposes.
* @param saveState defaults to true
* @return The current instance of the builder.
*/
public MongoItemReaderBuilder<T> saveState(boolean saveState) {
this.saveState = saveState;
return this;
}
/**
* The name used to calculate the key within the
* {@link org.springframework.batch.item.ExecutionContext}. Required if
* {@link #saveState(boolean)} is set to true.
* @param name name of the reader instance
* @return The current instance of the builder.
* @see org.springframework.batch.item.ItemStreamSupport#setName(String)
*/
public MongoItemReaderBuilder<T> name(String name) {
this.name = name;
return this;
}
/**
* Configure the max number of items to be read.
* @param maxItemCount the max items to be read
* @return The current instance of the builder.
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int)
*/
public MongoItemReaderBuilder<T> maxItemCount(int maxItemCount) {
this.maxItemCount = maxItemCount;
return this;
}
/**
* Index for the current item. Used on restarts to indicate where to start from.
* @param currentItemCount current index
* @return this instance for method chaining
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int)
*/
public MongoItemReaderBuilder<T> currentItemCount(int currentItemCount) {
this.currentItemCount = currentItemCount;
return this;
}
/**
* Used to perform operations against the MongoDB instance. Also handles the mapping
* of documents to objects.
* @param template the MongoOperations instance to use
* @see MongoOperations
* @return The current instance of the builder
* @see MongoItemReader#setTemplate(MongoOperations)
*/
public MongoItemReaderBuilder<T> template(MongoOperations template) {
this.template = template;
return this;
}
/**
* A JSON formatted MongoDB jsonQuery. Parameterization of the provided jsonQuery is
* allowed via ?&lt;index&gt; placeholders where the &lt;index&gt; indicates the index
* of the parameterValue to substitute.
* @param query JSON formatted Mongo jsonQuery
* @return The current instance of the builder
* @see MongoItemReader#setQuery(String)
*/
public MongoItemReaderBuilder<T> jsonQuery(String query) {
this.jsonQuery = query;
return this;
}
/**
* The type of object to be returned for each {@link MongoItemReader#read()} call.
* @param targetType the type of object to return
* @return The current instance of the builder
* @see MongoItemReader#setTargetType(Class)
*/
public MongoItemReaderBuilder<T> targetType(Class<? extends T> targetType) {
this.targetType = targetType;
return this;
}
/**
* {@link List} of values to be substituted in for each of the parameters in the
* query.
* @param parameterValues values
* @return The current instance of the builder
* @see MongoItemReader#setParameterValues(List)
*/
public MongoItemReaderBuilder<T> parameterValues(List<Object> parameterValues) {
this.parameterValues = parameterValues;
return this;
}
/**
* Values to be substituted in for each of the parameters in the query.
* @param parameterValues values
* @return The current instance of the builder
* @see MongoItemReader#setParameterValues(List)
*/
public MongoItemReaderBuilder<T> parameterValues(Object... parameterValues) {
return parameterValues(Arrays.asList(parameterValues));
}
/**
* JSON defining the fields to be returned from the matching documents by MongoDB.
* @param fields JSON string that identifies the fields to sort by.
* @return The current instance of the builder
* @see MongoItemReader#setFields(String)
*/
public MongoItemReaderBuilder<T> fields(String fields) {
this.fields = fields;
return this;
}
/**
* {@link Map} of property
* names/{@link org.springframework.data.domain.Sort.Direction} values to sort the
* input by.
* @param sorts map of properties and direction to sort each.
* @return The current instance of the builder
* @see MongoItemReader#setSort(Map)
*/
public MongoItemReaderBuilder<T> sorts(Map<String, Sort.Direction> sorts) {
this.sorts = sorts;
return this;
}
/**
* Establish an optional collection that can be queried.
* @param collection Mongo collection to be queried.
* @return The current instance of the builder
* @see MongoItemReader#setCollection(String)
*/
public MongoItemReaderBuilder<T> collection(String collection) {
this.collection = collection;
return this;
}
/**
* JSON String telling MongoDB what index to use.
* @param hint string indicating what index to use.
* @return The current instance of the builder
* @see MongoItemReader#setHint(String)
*/
public MongoItemReaderBuilder<T> hint(String hint) {
this.hint = hint;
return this;
}
/**
* The number of items to be read with each page.
* @param pageSize the number of items
* @return this instance for method chaining
* @see MongoItemReader#setPageSize(int)
*/
public MongoItemReaderBuilder<T> pageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}
/**
* Provide a Spring Data Mongo {@link Query}. This will take precedence over a JSON
* configured query.
* @param query Query to execute
* @return this instance for method chaining
* @see MongoItemReader#setQuery(Query)
*/
public MongoItemReaderBuilder<T> query(Query query) {
this.query = query;
return this;
}
/**
* Validates and builds a {@link MongoItemReader}.
* @return a {@link MongoItemReader}
*/
public MongoItemReader<T> build() {
Assert.notNull(this.template, "template is required.");
if (this.saveState) {
Assert.hasText(this.name, "A name is required when saveState is set to true");
}
Assert.notNull(this.targetType, "targetType is required.");
Assert.state(StringUtils.hasText(this.jsonQuery) || this.query != null, "A query is required");
if (StringUtils.hasText(this.jsonQuery) || this.query != null) {
Assert.notNull(this.sorts, "sorts map is required.");
}
MongoItemReader<T> reader = new MongoItemReader<>();
reader.setTemplate(this.template);
reader.setTargetType(this.targetType);
reader.setQuery(this.jsonQuery);
reader.setSort(this.sorts);
reader.setHint(this.hint);
reader.setFields(this.fields);
reader.setCollection(this.collection);
reader.setParameterValues(this.parameterValues);
reader.setQuery(this.query);
reader.setPageSize(this.pageSize);
reader.setName(this.name);
reader.setSaveState(this.saveState);
reader.setCurrentItemCount(this.currentItemCount);
reader.setMaxItemCount(this.maxItemCount);
return reader;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 the original author or authors.
* Copyright 2017-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,23 +37,6 @@ public class MongoItemWriterBuilder<T> {
private Mode mode = Mode.UPSERT;
/**
* Indicates if the items being passed to the writer are to be saved or removed from
* the data store. If set to false (default), the items will be saved. If set to true,
* the items will be removed.
* @param delete removal indicator
* @return The current instance of the builder
* @see MongoItemWriter#setDelete(boolean)
* @deprecated Use {@link MongoItemWriterBuilder#mode(Mode)} instead. Scheduled for
* removal in v5.3 or later.
*/
@Deprecated(since = "5.1", forRemoval = true)
public MongoItemWriterBuilder<T> delete(boolean delete) {
this.mode = (delete) ? Mode.REMOVE : Mode.UPSERT;
return this;
}
/**
* Set the operating {@link Mode} to be applied by this writer. Defaults to
* {@link Mode#UPSERT}.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 the original author or authors.
* Copyright 2017-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.springframework.batch.item.data.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -36,7 +37,35 @@ import org.springframework.util.StringUtils;
* @author Parikshit Dutta
* @since 5.1
*/
public class MongoPagingItemReaderBuilder<T> extends MongoItemReaderBuilder<T> {
public class MongoPagingItemReaderBuilder<T> {
protected MongoOperations template;
protected String jsonQuery;
protected Class<? extends T> targetType;
protected Map<String, Sort.Direction> sorts;
protected String hint;
protected String fields;
protected String collection;
protected List<Object> parameterValues = new ArrayList<>();
protected int pageSize = 10;
protected boolean saveState = true;
protected String name;
protected int maxItemCount = Integer.MAX_VALUE;
protected int currentItemCount;
protected Query query;
/**
* Configure if the state of the
@@ -228,7 +257,6 @@ public class MongoPagingItemReaderBuilder<T> extends MongoItemReaderBuilder<T> {
return this;
}
@Override
public MongoPagingItemReader<T> build() {
Assert.notNull(this.template, "template is required.");
if (this.saveState) {

View File

@@ -1,265 +0,0 @@
/*
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data.builder;
import java.util.Map;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemReader;
import org.springframework.util.Assert;
/**
* A builder for the {@link Neo4jItemReader}.
*
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
* @since 4.0
* @see Neo4jItemReader
* @deprecated since 5.0 in favor of the item reader builder from <a href=
* "https://github.com/spring-projects/spring-batch-extensions/blob/main/spring-batch-neo4j">...</a>
*/
@Deprecated
public class Neo4jItemReaderBuilder<T> {
private SessionFactory sessionFactory;
private String startStatement;
private String returnStatement;
private String matchStatement;
private String whereStatement;
private String orderByStatement;
private Class<T> targetType;
private Map<String, Object> parameterValues;
private int pageSize = 10;
private boolean saveState = true;
private String name;
private int maxItemCount = Integer.MAX_VALUE;
private int currentItemCount;
/**
* Configure if the state of the
* {@link org.springframework.batch.item.ItemStreamSupport} should be persisted within
* the {@link org.springframework.batch.item.ExecutionContext} for restart purposes.
* @param saveState defaults to true
* @return The current instance of the builder.
*/
public Neo4jItemReaderBuilder<T> saveState(boolean saveState) {
this.saveState = saveState;
return this;
}
/**
* The name used to calculate the key within the
* {@link org.springframework.batch.item.ExecutionContext}. Required if
* {@link #saveState(boolean)} is set to true.
* @param name name of the reader instance
* @return The current instance of the builder.
* @see org.springframework.batch.item.ItemStreamSupport#setName(String)
*/
public Neo4jItemReaderBuilder<T> name(String name) {
this.name = name;
return this;
}
/**
* Configure the max number of items to be read.
* @param maxItemCount the max items to be read
* @return The current instance of the builder.
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int)
*/
public Neo4jItemReaderBuilder<T> maxItemCount(int maxItemCount) {
this.maxItemCount = maxItemCount;
return this;
}
/**
* Index for the current item. Used on restarts to indicate where to start from.
* @param currentItemCount current index
* @return this instance for method chaining
* @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int)
*/
public Neo4jItemReaderBuilder<T> currentItemCount(int currentItemCount) {
this.currentItemCount = currentItemCount;
return this;
}
/**
* Establish the session factory for the reader.
* @param sessionFactory the factory to use for the reader.
* @return this instance for method chaining
* @see Neo4jItemReader#setSessionFactory(SessionFactory)
*/
public Neo4jItemReaderBuilder<T> sessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
return this;
}
/**
* The number of items to be read with each page.
* @param pageSize the number of items
* @return this instance for method chaining
* @see Neo4jItemReader#setPageSize(int)
*/
public Neo4jItemReaderBuilder<T> pageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}
/**
* Optional parameters to be used in the cypher query.
* @param parameterValues the parameter values to be used in the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setParameterValues(Map)
*/
public Neo4jItemReaderBuilder<T> parameterValues(Map<String, Object> parameterValues) {
this.parameterValues = parameterValues;
return this;
}
/**
* The start segment of the cypher query. START is prepended to the statement provided
* and should <em>not</em> be included.
* @param startStatement the start fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setStartStatement(String)
*/
public Neo4jItemReaderBuilder<T> startStatement(String startStatement) {
this.startStatement = startStatement;
return this;
}
/**
* The return statement of the cypher query. RETURN is prepended to the statement
* provided and should <em>not</em> be included
* @param returnStatement the return fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setReturnStatement(String)
*/
public Neo4jItemReaderBuilder<T> returnStatement(String returnStatement) {
this.returnStatement = returnStatement;
return this;
}
/**
* An optional match fragment of the cypher query. MATCH is prepended to the statement
* provided and should <em>not</em> be included.
* @param matchStatement the match fragment of the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setMatchStatement(String)
*/
public Neo4jItemReaderBuilder<T> matchStatement(String matchStatement) {
this.matchStatement = matchStatement;
return this;
}
/**
* An optional where fragment of the cypher query. WHERE is prepended to the statement
* provided and should <em>not</em> be included.
* @param whereStatement where fragment of the cypher query
* @return this instance for method chaining
* @see Neo4jItemReader#setWhereStatement(String)
*/
public Neo4jItemReaderBuilder<T> whereStatement(String whereStatement) {
this.whereStatement = whereStatement;
return this;
}
/**
* A list of properties to order the results by. This is required so that subsequent
* page requests pull back the segment of results correctly. ORDER BY is prepended to
* the statement provided and should <em>not</em> be included.
* @param orderByStatement order by fragment of the cypher query.
* @return this instance for method chaining
* @see Neo4jItemReader#setOrderByStatement(String)
*/
public Neo4jItemReaderBuilder<T> orderByStatement(String orderByStatement) {
this.orderByStatement = orderByStatement;
return this;
}
/**
* The object type to be returned from each call to {@link Neo4jItemReader#read()}
* @param targetType the type of object to return.
* @return this instance for method chaining
* @see Neo4jItemReader#setTargetType(Class)
*/
public Neo4jItemReaderBuilder<T> targetType(Class<T> targetType) {
this.targetType = targetType;
return this;
}
/**
* Returns a fully constructed {@link Neo4jItemReader}.
* @return a new {@link Neo4jItemReader}
*/
public Neo4jItemReader<T> build() {
if (this.saveState) {
Assert.hasText(this.name, "A name is required when saveState is set to true");
}
Assert.notNull(this.sessionFactory, "sessionFactory is required.");
Assert.notNull(this.targetType, "targetType is required.");
Assert.hasText(this.startStatement, "startStatement is required.");
Assert.hasText(this.returnStatement, "returnStatement is required.");
Assert.hasText(this.orderByStatement, "orderByStatement is required.");
Assert.isTrue(this.pageSize > 0, "pageSize must be greater than zero");
Assert.isTrue(this.maxItemCount > 0, "maxItemCount must be greater than zero");
Assert.isTrue(this.maxItemCount > this.currentItemCount, "maxItemCount must be greater than currentItemCount");
Neo4jItemReader<T> reader = new Neo4jItemReader<>();
reader.setMatchStatement(this.matchStatement);
reader.setOrderByStatement(this.orderByStatement);
reader.setPageSize(this.pageSize);
reader.setParameterValues(this.parameterValues);
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(this.targetType);
reader.setStartStatement(this.startStatement);
reader.setReturnStatement(this.returnStatement);
reader.setWhereStatement(this.whereStatement);
reader.setName(this.name);
reader.setSaveState(this.saveState);
reader.setCurrentItemCount(this.currentItemCount);
reader.setMaxItemCount(this.maxItemCount);
return reader;
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2017-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data.builder;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemWriter;
import org.springframework.util.Assert;
/**
* A builder implementation for the {@link Neo4jItemWriter}
*
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
* @since 4.0
* @see Neo4jItemWriter
* @deprecated since 5.0 in favor of the item writer builder from <a href=
* "https://github.com/spring-projects/spring-batch-extensions/blob/main/spring-batch-neo4j">...</a>
*/
@Deprecated
public class Neo4jItemWriterBuilder<T> {
private boolean delete = false;
private SessionFactory sessionFactory;
/**
* Boolean flag indicating whether the writer should save or delete the item at write
* time.
* @param delete true if write should delete item, false if item should be saved.
* Default is false.
* @return The current instance of the builder
* @see Neo4jItemWriter#setDelete(boolean)
*/
public Neo4jItemWriterBuilder<T> delete(boolean delete) {
this.delete = delete;
return this;
}
/**
* Establish the session factory that will be used to create {@link Session} instances
* for interacting with Neo4j.
* @param sessionFactory sessionFactory to be used.
* @return The current instance of the builder
* @see Neo4jItemWriter#setSessionFactory(SessionFactory)
*/
public Neo4jItemWriterBuilder<T> sessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
return this;
}
/**
* Validates and builds a {@link org.springframework.batch.item.data.Neo4jItemWriter}.
* @return a {@link Neo4jItemWriter}
*/
public Neo4jItemWriter<T> build() {
Assert.notNull(sessionFactory, "sessionFactory is required.");
Neo4jItemWriter<T> writer = new Neo4jItemWriter<>();
writer.setDelete(this.delete);
writer.setSessionFactory(this.sessionFactory);
return writer;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2024 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,36 +61,6 @@ public abstract class SqlPagingQueryUtils {
return sql.toString();
}
/**
* Generate SQL query string using a LIMIT clause
* @param provider {@link AbstractSqlPagingQueryProvider} providing the implementation
* specifics
* @param remainingPageQuery is this query for the remaining pages (true) as opposed
* to the first page (false)
* @param limitClause the implementation specific limit clause to be used
* @return the generated query
* @deprecated as of v5.0 in favor of
* {@link #generateLimitGroupedSqlQuery(AbstractSqlPagingQueryProvider, java.lang.String)}
*/
@Deprecated
public static String generateLimitGroupedSqlQuery(AbstractSqlPagingQueryProvider provider,
boolean remainingPageQuery, String limitClause) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * ");
sql.append(" FROM (");
sql.append("SELECT ").append(provider.getSelectClause());
sql.append(" FROM ").append(provider.getFromClause());
sql.append(provider.getWhereClause() == null ? "" : " WHERE " + provider.getWhereClause());
buildGroupByClause(provider, sql);
sql.append(") AS MAIN_QRY ");
sql.append("WHERE ");
buildSortConditions(provider, sql);
sql.append(" ORDER BY ").append(buildSortClause(provider));
sql.append(" ").append(limitClause);
return sql.toString();
}
/**
* Generate SQL query string using a LIMIT clause
* @param provider {@link AbstractSqlPagingQueryProvider} providing the implementation
@@ -136,34 +106,6 @@ public abstract class SqlPagingQueryUtils {
return sql.toString();
}
/**
* Generate SQL query string using a TOP clause
* @param provider {@link AbstractSqlPagingQueryProvider} providing the implementation
* specifics
* @param remainingPageQuery is this query for the remaining pages (true) as opposed
* to the first page (false)
* @param topClause the implementation specific top clause to be used
* @return the generated query
* @deprecated since v5.2 in favor of
* {@link #generateGroupedTopSqlQuery(AbstractSqlPagingQueryProvider, String)}
*/
@Deprecated
public static String generateGroupedTopSqlQuery(AbstractSqlPagingQueryProvider provider, boolean remainingPageQuery,
String topClause) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ").append(topClause).append(" * FROM (");
sql.append("SELECT ").append(provider.getSelectClause());
sql.append(" FROM ").append(provider.getFromClause());
sql.append(provider.getWhereClause() == null ? "" : " WHERE " + provider.getWhereClause());
buildGroupByClause(provider, sql);
sql.append(") AS MAIN_QRY ");
sql.append("WHERE ");
buildSortConditions(provider, sql);
sql.append(" ORDER BY ").append(buildSortClause(provider));
return sql.toString();
}
/**
* Generate SQL query string using a TOP clause
* @param provider {@link AbstractSqlPagingQueryProvider} providing the implementation

View File

@@ -1,130 +0,0 @@
/*
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.database.support;
import org.springframework.util.StringUtils;
/**
* Generic Paging Query Provider using standard SQL:2003 windowing functions. These
* features are supported by DB2, Oracle, SQL Server 2005, Sybase and Apache Derby version
* 10.4.1.3
*
* @author Thomas Risberg
* @author Michael Minella
* @since 2.0
* @deprecated since 5.2.1 with no replacement. Scheduled for removal in 6.0.
*/
@Deprecated(forRemoval = true)
public class SqlWindowingPagingQueryProvider extends AbstractSqlPagingQueryProvider {
@Override
public String generateFirstPageQuery(int pageSize) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM ( ");
sql.append("SELECT ")
.append(StringUtils.hasText(getOrderedQueryAlias()) ? getOrderedQueryAlias() + ".*, " : "*, ");
sql.append("ROW_NUMBER() OVER (").append(getOverClause());
sql.append(") AS ROW_NUMBER");
sql.append(getOverSubstituteClauseStart());
sql.append(" FROM ")
.append(getFromClause())
.append(getWhereClause() == null ? "" : " WHERE " + getWhereClause());
sql.append(getGroupClause() == null ? "" : " GROUP BY " + getGroupClause());
sql.append(getOverSubstituteClauseEnd());
sql.append(") ")
.append(getSubQueryAlias())
.append("WHERE ")
.append(extractTableAlias())
.append("ROW_NUMBER <= ")
.append(pageSize);
sql.append(" ORDER BY ").append(SqlPagingQueryUtils.buildSortClause(this));
return sql.toString();
}
protected String getOrderedQueryAlias() {
return "";
}
protected Object getSubQueryAlias() {
return "AS TMP_SUB ";
}
protected Object extractTableAlias() {
String alias = String.valueOf(getSubQueryAlias());
if (StringUtils.hasText(alias) && alias.toUpperCase().startsWith("AS")) {
alias = alias.substring(3).trim() + ".";
}
return alias;
}
@Override
public String generateRemainingPagesQuery(int pageSize) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM ( ");
sql.append("SELECT ")
.append(StringUtils.hasText(getOrderedQueryAlias()) ? getOrderedQueryAlias() + ".*, " : "*, ");
sql.append("ROW_NUMBER() OVER (").append(getOverClause());
sql.append(") AS ROW_NUMBER");
sql.append(getOverSubstituteClauseStart());
sql.append(" FROM ").append(getFromClause());
if (getWhereClause() != null) {
sql.append(" WHERE ");
sql.append(getWhereClause());
}
sql.append(getGroupClause() == null ? "" : " GROUP BY " + getGroupClause());
sql.append(getOverSubstituteClauseEnd());
sql.append(") ")
.append(getSubQueryAlias())
.append("WHERE ")
.append(extractTableAlias())
.append("ROW_NUMBER <= ")
.append(pageSize);
sql.append(" AND ");
SqlPagingQueryUtils.buildSortConditions(this, sql);
sql.append(" ORDER BY ").append(SqlPagingQueryUtils.buildSortClause(this));
return sql.toString();
}
protected String getOverClause() {
StringBuilder sql = new StringBuilder();
sql.append(" ORDER BY ").append(buildSortClause(this));
return sql.toString();
}
protected String getOverSubstituteClauseStart() {
return "";
}
protected String getOverSubstituteClauseEnd() {
return "";
}
/**
* Generates ORDER BY attributes based on the sort keys.
* @param provider the paging query provider
* @return a String that can be appended to an ORDER BY clause.
*/
private String buildSortClause(AbstractSqlPagingQueryProvider provider) {
return SqlPagingQueryUtils.buildSortClause(provider.getSortKeysWithoutAliases());
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2006-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.repeat.listener;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatListener;
/**
* Empty method implementation of {@link RepeatListener}.
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @deprecated as of v5.0 in favor of the default methods in {@link RepeatListener}.
*/
@Deprecated
public class RepeatListenerSupport implements RepeatListener {
@Override
public void before(RepeatContext context) {
}
@Override
public void after(RepeatContext context, RepeatStatus result) {
}
@Override
public void close(RepeatContext context) {
}
@Override
public void onError(RepeatContext context, Throwable e) {
}
@Override
public void open(RepeatContext context) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,25 +59,6 @@ public class TaskExecutorRepeatTemplate extends RepeatTemplate {
private TaskExecutor taskExecutor = new SyncTaskExecutor();
/**
* Public setter for the throttle limit. The throttle limit is the largest number of
* concurrent tasks that can be executing at one time - if a new task arrives and the
* throttle limit is breached we wait for one of the executing tasks to finish before
* submitting the new one to the {@link TaskExecutor}. Default value is
* {@link #DEFAULT_THROTTLE_LIMIT}. N.B. when used with a thread pooled
* {@link TaskExecutor} the thread pool might prevent the throttle limit actually
* being reached (so make the core pool size larger than the throttle limit if
* possible).
* @param throttleLimit the throttleLimit to set.
* @deprecated since 5.0, scheduled for removal in 6.0. Use a pooled
* {@link TaskExecutor} implemenation with a limited capacity of its task queue
* instead.
*/
@Deprecated(since = "5.0", forRemoval = true)
public void setThrottleLimit(int throttleLimit) {
this.throttleLimit = throttleLimit;
}
/**
* Setter for task executor to be used to run the individual item callbacks.
* @param taskExecutor a TaskExecutor

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.support;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Helper class that sets up a System property with a default value. A System property is
* created with the specified key name, and default value (i.e. if the property already
* exists it is not changed).
*
* @author Dave Syer
* @deprecated since 5.2 with no replacement.
*/
@Deprecated(since = "5.2.0", forRemoval = true)
public class SystemPropertyInitializer implements InitializingBean {
/**
* Name of system property used by default.
*/
public static final String ENVIRONMENT = "org.springframework.batch.support.SystemPropertyInitializer.ENVIRONMENT";
private String keyName = ENVIRONMENT;
private String defaultValue;
/**
* Set the key name for the System property that is created. Defaults to
* {@link #ENVIRONMENT}.
* @param keyName the key name to set
*/
public void setKeyName(String keyName) {
this.keyName = keyName;
}
/**
* Mandatory property specifying the default value of the System property.
* @param defaultValue the default value to set
*/
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
/**
* Sets the System property with the provided name and default value.
*
* @see InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(defaultValue != null || System.getProperty(keyName) != null,
"Either a default value must be specified or the value should already be set for System property: "
+ keyName);
System.setProperty(keyName, System.getProperty(keyName, defaultValue));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2022 the original author or authors.
* Copyright 2013-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -199,7 +199,7 @@ class MongoItemWriterTests {
@Test
void testRemoveNoObjectIdNoCollection() throws Exception {
writer.setDelete(true);
writer.setMode(Mode.REMOVE);
Chunk<Object> items = Chunk.of(new Item("Foo"), new Item("Bar"));
writer.write(items);
@@ -210,7 +210,7 @@ class MongoItemWriterTests {
@Test
void testRemoveNoObjectIdWithCollection() throws Exception {
writer.setDelete(true);
writer.setMode(Mode.REMOVE);
Chunk<Object> items = Chunk.of(new Item("Foo"), new Item("Bar"));
writer.setCollection("collection");
@@ -222,7 +222,7 @@ class MongoItemWriterTests {
@Test
void testRemoveNoTransactionNoCollection() throws Exception {
writer.setDelete(true);
writer.setMode(Mode.REMOVE);
Chunk<Object> items = Chunk.of(new Item(1), new Item(2));
writer.write(items);
@@ -233,7 +233,7 @@ class MongoItemWriterTests {
@Test
void testRemoveNoTransactionWithCollection() throws Exception {
writer.setDelete(true);
writer.setMode(Mode.REMOVE);
Chunk<Object> items = Chunk.of(new Item(1), new Item(2));
writer.setCollection("collection");

View File

@@ -1,171 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;
@SuppressWarnings("deprecation")
@ExtendWith(MockitoExtension.class)
class Neo4jItemReaderTests {
@Mock
private Iterable<String> result;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
private Neo4jItemReader<String> buildSessionBasedReader() throws Exception {
Neo4jItemReader<String> reader = new Neo4jItemReader<>();
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(String.class);
reader.setStartStatement("n=node(*)");
reader.setReturnStatement("*");
reader.setOrderByStatement("n.age");
reader.setPageSize(50);
reader.afterPropertiesSet();
return reader;
}
@Test
void testAfterPropertiesSet() throws Exception {
Neo4jItemReader<String> reader = new Neo4jItemReader<>();
Exception exception = assertThrows(IllegalStateException.class, reader::afterPropertiesSet);
assertEquals("A SessionFactory is required", exception.getMessage());
reader.setSessionFactory(this.sessionFactory);
exception = assertThrows(IllegalStateException.class, reader::afterPropertiesSet);
assertEquals("The type to be returned is required", exception.getMessage());
reader.setTargetType(String.class);
exception = assertThrows(IllegalStateException.class, reader::afterPropertiesSet);
assertEquals("A START statement is required", exception.getMessage());
reader.setStartStatement("n=node(*)");
exception = assertThrows(IllegalStateException.class, reader::afterPropertiesSet);
assertEquals("A RETURN statement is required", exception.getMessage());
reader.setReturnStatement("n.name, n.phone");
exception = assertThrows(IllegalStateException.class, reader::afterPropertiesSet);
assertEquals("A ORDER BY statement is required", exception.getMessage());
reader.setOrderByStatement("n.age");
reader.afterPropertiesSet();
reader = new Neo4jItemReader<>();
reader.setSessionFactory(this.sessionFactory);
reader.setTargetType(String.class);
reader.setStartStatement("n=node(*)");
reader.setReturnStatement("n.name, n.phone");
reader.setOrderByStatement("n.age");
reader.afterPropertiesSet();
}
@Test
void testNullResultsWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
ArgumentCaptor<String> query = ArgumentCaptor.forClass(String.class);
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(eq(String.class), query.capture(), isNull())).thenReturn(null);
assertFalse(itemReader.doPageRead().hasNext());
assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
}
@Test
void testNoResultsWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
ArgumentCaptor<String> query = ArgumentCaptor.forClass(String.class);
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(eq(String.class), query.capture(), isNull())).thenReturn(result);
when(result.iterator()).thenReturn(Collections.emptyIterator());
assertFalse(itemReader.doPageRead().hasNext());
assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
}
@Test
void testResultsWithMatchAndWhereWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
itemReader.setMatchStatement("n -- m");
itemReader.setWhereStatement("has(n.name)");
itemReader.setReturnStatement("m");
itemReader.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertTrue(itemReader.doPageRead().hasNext());
}
@Test
void testResultsWithMatchAndWhereWithParametersWithSession() throws Exception {
Neo4jItemReader<String> itemReader = buildSessionBasedReader();
Map<String, Object> params = new HashMap<>();
params.put("foo", "bar");
itemReader.setParameterValues(params);
itemReader.setMatchStatement("n -- m");
itemReader.setWhereStatement("has(n.name)");
itemReader.setReturnStatement("m");
itemReader.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", params))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertTrue(itemReader.doPageRead().hasNext());
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.Chunk;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@SuppressWarnings("deprecation")
@MockitoSettings(strictness = Strictness.LENIENT)
class Neo4jItemWriterTests {
private Neo4jItemWriter<String> writer;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
void testAfterPropertiesSet() throws Exception {
writer = new Neo4jItemWriter<>();
Exception exception = assertThrows(IllegalStateException.class, writer::afterPropertiesSet);
assertEquals("A SessionFactory is required", exception.getMessage());
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
}
@Test
void testWriteNoItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(new Chunk<>());
verifyNoInteractions(this.session);
}
@Test
void testWriteItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
Chunk<String> items = new Chunk<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).save("foo");
verify(this.session).save("bar");
}
@Test
void testDeleteItemsWithSession() throws Exception {
writer = new Neo4jItemWriter<>();
writer.setSessionFactory(this.sessionFactory);
writer.afterPropertiesSet();
Chunk<String> items = new Chunk<>();
items.add("foo");
items.add("bar");
writer.setDelete(true);
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).delete("foo");
verify(this.session).delete("bar");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -112,7 +112,9 @@ class MongoItemWriterBuilderTests {
@Test
void testDelete() throws Exception {
MongoItemWriter<Item> writer = new MongoItemWriterBuilder<Item>().template(this.template).delete(true).build();
MongoItemWriter<Item> writer = new MongoItemWriterBuilder<Item>().template(this.template)
.mode(MongoItemWriter.Mode.REMOVE)
.build();
writer.write(this.removeItems);

View File

@@ -1,258 +0,0 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data.builder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.data.Neo4jItemReader;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
/**
* @author Glenn Renfro
*/
@SuppressWarnings("deprecation")
@ExtendWith(MockitoExtension.class)
class Neo4jItemReaderBuilderTests {
@Mock
private Iterable<String> result;
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
void testFullyQualifiedItemReader() throws Exception {
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m")
.build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("foo", itemReader.read(), "The expected value was not returned by reader.");
assertEquals("bar", itemReader.read(), "The expected value was not returned by reader.");
assertEquals("baz", itemReader.read(), "The expected value was not returned by reader.");
}
@Test
void testCurrentSize() throws Exception {
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.returnStatement("m")
.currentItemCount(0)
.maxItemCount(1)
.build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class, "START n=node(*) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("foo", itemReader.read(), "The expected value was not returned by reader.");
assertNull(itemReader.read(), "The expected value was not should be null.");
}
@Test
void testResultsWithMatchAndWhereWithParametersWithSession() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("foo", "bar");
Neo4jItemReader<String> itemReader = new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("foo")
.parameterValues(params)
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m")
.build();
when(this.sessionFactory.openSession()).thenReturn(this.session);
when(this.session.query(String.class,
"START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", params))
.thenReturn(result);
when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
assertEquals("foo", itemReader.read(), "The expected value was not returned by reader.");
}
@Test
void testNoSessionFactory() {
var builder = new Neo4jItemReaderBuilder<String>().targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("bar");
Exception exception = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("sessionFactory is required.", exception.getMessage());
}
@Test
void testZeroPageSize() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(0)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "pageSize must be greater than zero");
}
@Test
void testZeroMaxItemCount() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(5)
.maxItemCount(0)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "maxItemCount must be greater than zero");
}
@Test
void testCurrentItemCountGreaterThanMaxItemCount() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(5)
.maxItemCount(5)
.currentItemCount(6)
.name("foo")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "maxItemCount must be greater than currentItemCount");
}
@Test
void testNullName() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50), "A name is required when saveState is set to true");
// tests that name is not required if saveState is set to false.
new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.saveState(false)
.pageSize(50)
.build();
}
@Test
void testNullTargetType() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.startStatement("n=node(*)")
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "targetType is required.");
}
@Test
void testNullStartStatement() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.returnStatement("*")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "startStatement is required.");
}
@Test
void testNullReturnStatement() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.orderByStatement("n.age")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)"), "returnStatement is required.");
}
@Test
void testNullOrderByStatement() {
validateExceptionMessage(new Neo4jItemReaderBuilder<String>().sessionFactory(this.sessionFactory)
.targetType(String.class)
.startStatement("n=node(*)")
.returnStatement("*")
.pageSize(50)
.name("bar")
.matchStatement("n -- m")
.whereStatement("has(n.name)")
.returnStatement("m"), "orderByStatement is required.");
}
private void validateExceptionMessage(Neo4jItemReaderBuilder<?> builder, String message) {
Exception exception = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals(message, exception.getMessage());
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.data.builder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.data.Neo4jItemWriter;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Glenn Renfro
* @author Mahmoud Ben Hassine
*/
@SuppressWarnings("deprecation")
@ExtendWith(MockitoExtension.class)
class Neo4jItemWriterBuilderTests {
@Mock
private SessionFactory sessionFactory;
@Mock
private Session session;
@Test
void testBasicWriter() throws Exception {
Neo4jItemWriter<String> writer = new Neo4jItemWriterBuilder<String>().sessionFactory(this.sessionFactory)
.build();
Chunk<String> items = new Chunk<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).save("foo");
verify(this.session).save("bar");
verify(this.session, never()).delete("foo");
verify(this.session, never()).delete("bar");
}
@Test
void testBasicDelete() throws Exception {
Neo4jItemWriter<String> writer = new Neo4jItemWriterBuilder<String>().delete(true)
.sessionFactory(this.sessionFactory)
.build();
Chunk<String> items = new Chunk<>();
items.add("foo");
items.add("bar");
when(this.sessionFactory.openSession()).thenReturn(this.session);
writer.write(items);
verify(this.session).delete("foo");
verify(this.session).delete("bar");
verify(this.session, never()).save("foo");
verify(this.session, never()).save("bar");
}
@Test
void testNoSessionFactory() {
Exception exception = assertThrows(IllegalArgumentException.class,
() -> new Neo4jItemWriterBuilder<String>().build());
assertEquals("sessionFactory is required.", exception.getMessage());
}
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2006-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.database.support;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Thomas Risberg
* @author Michael Minella
*/
class SqlWindowingPagingQueryProviderTests extends AbstractSqlPagingQueryProviderTests {
@SuppressWarnings("removal")
SqlWindowingPagingQueryProviderTests() {
pagingQueryProvider = new SqlWindowingPagingQueryProvider();
}
@Test
@Override
void testGenerateFirstPageQuery() {
String sql = "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 ORDER BY id ASC";
String s = pagingQueryProvider.generateFirstPageQuery(pageSize);
assertEquals(sql, s);
}
@Test
@Override
void testGenerateRemainingPagesQuery() {
String sql = "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 AND ((id > ?)) ORDER BY id ASC";
String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize);
assertEquals(sql, s);
}
@Test
@Override
void testGenerateFirstPageQueryWithGroupBy() {
pagingQueryProvider.setGroupClause("dep");
String sql = "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1 GROUP BY dep) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 ORDER BY id ASC";
String s = pagingQueryProvider.generateFirstPageQuery(pageSize);
assertEquals(sql, s);
}
@Test
@Override
void testGenerateRemainingPagesQueryWithGroupBy() {
pagingQueryProvider.setGroupClause("dep");
String sql = "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY id ASC) AS ROW_NUMBER FROM foo WHERE bar = 1 GROUP BY dep) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 AND ((id > ?)) ORDER BY id ASC";
String s = pagingQueryProvider.generateRemainingPagesQuery(pageSize);
assertEquals(sql, s);
}
@Override
String getFirstPageSqlWithMultipleSortKeys() {
return "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY name ASC, id DESC) AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 ORDER BY name ASC, id DESC";
}
@Override
String getRemainingSqlWithMultipleSortKeys() {
return "SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( ORDER BY name ASC, id DESC) AS ROW_NUMBER FROM foo WHERE bar = 1) AS TMP_SUB WHERE TMP_SUB.ROW_NUMBER <= 100 AND ((name > ?) OR (name = ? AND id < ?)) ORDER BY name ASC, id DESC";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -159,7 +159,6 @@ class TaskExecutorRepeatTemplateAsynchronousTests extends AbstractTradeBatchTest
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.setConcurrencyLimit(300);
template.setTaskExecutor(taskExecutor);
template.setThrottleLimit(throttleLimit);
String threadName = Thread.currentThread().getName();
Set<String> threadNames = ConcurrentHashMap.newKeySet();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2023 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,12 +18,10 @@ package org.springframework.batch.repeat.support;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
@@ -35,7 +33,6 @@ import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.repeat.policy.SimpleCompletionPolicy;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
@@ -54,8 +51,6 @@ class TaskExecutorRepeatTemplateBulkAsynchronousTests {
private int total = 1000;
private int throttleLimit = 30;
private volatile int early = Integer.MAX_VALUE;
private volatile int error = Integer.MAX_VALUE;
@@ -77,7 +72,6 @@ class TaskExecutorRepeatTemplateBulkAsynchronousTests {
threadPool.setQueueCapacity(0);
threadPool.afterPropertiesSet();
template.setTaskExecutor(threadPool);
template.setThrottleLimit(throttleLimit);
items = Collections.synchronizedList(new ArrayList<>());
@@ -117,102 +111,6 @@ class TaskExecutorRepeatTemplateBulkAsynchronousTests {
threadPool.destroy();
}
@Test
void testThrottleLimit() {
template.iterate(callback);
int frequency = Collections.frequency(items, null);
assertEquals(total, items.size() - frequency);
assertTrue(frequency > 1);
assertTrue(frequency <= throttleLimit + 1);
}
@Test
void testThrottleLimitEarlyFinish() {
early = 2;
template.iterate(callback);
int frequency = Collections.frequency(items, null);
assertEquals(total, items.size() - frequency);
assertTrue(frequency > 1);
assertTrue(frequency <= throttleLimit + 1);
}
@Test
void testThrottleLimitEarlyFinishThreadStarvation() {
early = 2;
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// Set the concurrency limit below the throttle limit for possible
// starvation condition
taskExecutor.setMaxPoolSize(20);
taskExecutor.setCorePoolSize(10);
taskExecutor.setQueueCapacity(0);
// This is the most sensible setting, otherwise the bookkeeping in
// ResultHolderResultQueue gets out of whack when tasks are aborted.
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.afterPropertiesSet();
template.setTaskExecutor(taskExecutor);
template.iterate(callback);
int frequency = Collections.frequency(items, null);
// Extra tasks will be submitted before the termination is detected
assertEquals(total, items.size() - frequency);
assertTrue(frequency <= throttleLimit + 1);
taskExecutor.destroy();
}
@Test
void testThrottleLimitEarlyFinishOneThread() {
early = 4;
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.setConcurrencyLimit(1);
// This is kind of slow with only one thread, so reduce size:
throttleLimit = 10;
total = 20;
template.setThrottleLimit(throttleLimit);
template.setTaskExecutor(taskExecutor);
template.iterate(callback);
int frequency = Collections.frequency(items, null);
assertEquals(total, items.size() - frequency);
assertTrue(frequency <= throttleLimit + 1);
}
@Test
void testThrottleLimitWithEarlyCompletion() {
early = 2;
template.setCompletionPolicy(new SimpleCompletionPolicy(10));
template.iterate(callback);
int frequency = Collections.frequency(items, null);
assertEquals(10, items.size() - frequency);
assertEquals(0, frequency);
}
@Test
void testThrottleLimitWithError() {
error = 50;
Exception exception = assertThrows(Exception.class, () -> template.iterate(callback));
assertEquals("Planned", exception.getMessage());
int frequency = Collections.frequency(items, null);
assertEquals(0, frequency);
}
@Test
void testErrorThrownByCallback() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2006-2022 the original author or authors.
* Copyright 2006-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,9 @@
package org.springframework.batch.repeat.support;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
/**
* @author Dave Syer
* @author Mahmoud Ben Hassine
*/
public class TaskExecutorRepeatTemplateTests extends SimpleRepeatTemplateTests {
@@ -30,10 +27,4 @@ public class TaskExecutorRepeatTemplateTests extends SimpleRepeatTemplateTests {
return new TaskExecutorRepeatTemplate();
}
@Test
void testSetThrottleLimit() {
// no check for illegal values
assertDoesNotThrow(() -> new TaskExecutorRepeatTemplate().setThrottleLimit(-1));
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2006-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.support;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* @author Dave Syer
*
*/
class SystemPropertyInitializerTests {
private static final String SIMPLE_NAME = SystemPropertyInitializerTests.class.getSimpleName();
private final SystemPropertyInitializer initializer = new SystemPropertyInitializer();
@BeforeEach
@AfterEach
void initializeProperty() {
System.clearProperty(SystemPropertyInitializer.ENVIRONMENT);
System.clearProperty(SIMPLE_NAME);
}
@Test
void testSetKeyName() throws Exception {
initializer.setKeyName(SIMPLE_NAME);
System.setProperty(SIMPLE_NAME, "foo");
initializer.afterPropertiesSet();
assertEquals("foo", System.getProperty(SIMPLE_NAME));
}
@Test
void testSetDefaultValue() throws Exception {
initializer.setDefaultValue("foo");
initializer.afterPropertiesSet();
assertEquals("foo", System.getProperty(SystemPropertyInitializer.ENVIRONMENT));
}
@Test
void testNoDefaultValue() {
assertThrows(IllegalStateException.class, initializer::afterPropertiesSet);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2023 the original author or authors.
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,17 +82,6 @@ public class RemoteChunkingManagerStepBuilder<I, O> extends FaultTolerantStepBui
private long throttleLimit = DEFAULT_THROTTLE_LIMIT;
/**
* Create a new {@link RemoteChunkingManagerStepBuilder}.
* @param stepName name of the manager step
* @deprecated use
* {@link RemoteChunkingManagerStepBuilder#RemoteChunkingManagerStepBuilder(String, JobRepository)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public RemoteChunkingManagerStepBuilder(String stepName) {
super(new StepBuilder(stepName));
}
/**
* Create a new {@link RemoteChunkingManagerStepBuilder}.
* @param stepName name of the manager step
@@ -228,21 +217,6 @@ public class RemoteChunkingManagerStepBuilder<I, O> extends FaultTolerantStepBui
return this;
}
/**
* Set the job repository
* @param jobRepository the repository to set
* @return this to enable fluent chaining
* @deprecated use
* {@link RemoteChunkingManagerStepBuilder#RemoteChunkingManagerStepBuilder(String, JobRepository)}
*/
@Override
@SuppressWarnings("removal")
@Deprecated(since = "5.1", forRemoval = true)
public RemoteChunkingManagerStepBuilder<I, O> repository(JobRepository jobRepository) {
super.repository(jobRepository);
return this;
}
@Override
public RemoteChunkingManagerStepBuilder<I, O> transactionManager(PlatformTransactionManager transactionManager) {
super.transactionManager(transactionManager);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2023 the original author or authors.
* Copyright 2019-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,17 +78,6 @@ public class RemotePartitioningManagerStepBuilder extends PartitionStepBuilder {
private long timeout = DEFAULT_TIMEOUT;
/**
* Create a new {@link RemotePartitioningManagerStepBuilder}.
* @param stepName name of the manager step
* @deprecated use
* {@link RemotePartitioningManagerStepBuilder#RemotePartitioningManagerStepBuilder(String, JobRepository)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public RemotePartitioningManagerStepBuilder(String stepName) {
super(new StepBuilder(stepName));
}
/**
* Create a new {@link RemotePartitioningManagerStepBuilder}.
* @param stepName name of the manager step
@@ -243,21 +232,6 @@ public class RemotePartitioningManagerStepBuilder extends PartitionStepBuilder {
return this.inputChannel == null;
}
/**
* Set the job repository
* @param jobRepository the repository to set
* @return this to enable fluent chaining
* @deprecated use
* {@link RemotePartitioningManagerStepBuilder#RemotePartitioningManagerStepBuilder(String, JobRepository)}
*/
@Override
@SuppressWarnings("removal")
@Deprecated(since = "5.1", forRemoval = true)
public RemotePartitioningManagerStepBuilder repository(JobRepository jobRepository) {
super.repository(jobRepository);
return this;
}
@Override
public RemotePartitioningManagerStepBuilder partitioner(String workerStepName, Partitioner partitioner) {
super.partitioner(workerStepName, partitioner);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2023 the original author or authors.
* Copyright 2018-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -80,17 +80,6 @@ public class RemotePartitioningWorkerStepBuilder extends StepBuilder {
private BeanFactory beanFactory;
/**
* Initialize a step builder for a step with the given name.
* @param name the name of the step
* @deprecated use
* {@link RemotePartitioningWorkerStepBuilder#RemotePartitioningWorkerStepBuilder(String, JobRepository)}
*/
@Deprecated(since = "5.0", forRemoval = true)
public RemotePartitioningWorkerStepBuilder(String name) {
super(name);
}
/**
* Initialize a step builder for a step with the given name.
* @param name the name of the step
@@ -157,21 +146,6 @@ public class RemotePartitioningWorkerStepBuilder extends StepBuilder {
return this;
}
/**
* Set the job repository
* @param jobRepository the repository to set
* @return this to enable fluent chaining
* @deprecated use
* {@link RemotePartitioningWorkerStepBuilder#RemotePartitioningWorkerStepBuilder(String, JobRepository)}
*/
@Override
@SuppressWarnings("removal")
@Deprecated(since = "5.1", forRemoval = true)
public RemotePartitioningWorkerStepBuilder repository(JobRepository jobRepository) {
super.repository(jobRepository);
return this;
}
@Override
public RemotePartitioningWorkerStepBuilder startLimit(int startLimit) {
super.startLimit(startLimit);
@@ -196,39 +170,18 @@ public class RemotePartitioningWorkerStepBuilder extends StepBuilder {
return this;
}
@Deprecated(since = "5.0", forRemoval = true)
@Override
public TaskletStepBuilder tasklet(Tasklet tasklet) {
configureWorkerIntegrationFlow();
return super.tasklet(tasklet);
}
@Override
public TaskletStepBuilder tasklet(Tasklet tasklet, PlatformTransactionManager transactionManager) {
configureWorkerIntegrationFlow();
return super.tasklet(tasklet, transactionManager);
}
@Deprecated(since = "5.0", forRemoval = true)
@Override
public <I, O> SimpleStepBuilder<I, O> chunk(int chunkSize) {
configureWorkerIntegrationFlow();
return super.chunk(chunkSize);
}
@Override
public <I, O> SimpleStepBuilder<I, O> chunk(int chunkSize, PlatformTransactionManager transactionManager) {
configureWorkerIntegrationFlow();
return super.chunk(chunkSize, transactionManager);
}
@Deprecated(since = "5.0", forRemoval = true)
@Override
public <I, O> SimpleStepBuilder<I, O> chunk(CompletionPolicy completionPolicy) {
configureWorkerIntegrationFlow();
return super.chunk(completionPolicy);
}
@Override
public <I, O> SimpleStepBuilder<I, O> chunk(CompletionPolicy completionPolicy,
PlatformTransactionManager transactionManager) {

View File

@@ -603,7 +603,7 @@ class without any argument to start the sample.
### MongoDB sample
This sample is a showcase of MongoDB support in Spring Batch. It copies data from
an input collection to an output collection using `MongoItemReader` and `MongoItemWriter`.
an input collection to an output collection using `MongoPagingItemReader` and `MongoItemWriter`.
To run the sample, you need to have a MongoDB server up and running on `localhost:27017`
(you can change these defaults in `mongodb-sample.properties`). If you use docker,

View File

@@ -25,5 +25,4 @@
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>
</beans>

View File

@@ -11,13 +11,13 @@
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager" p:lobHandler-ref="lobHandler"/>
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager"/>
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"

View File

@@ -11,13 +11,13 @@
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<bean class="org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager" p:lobHandler-ref="lobHandler"/>
p:dataSource-ref="dataSource" p:transactionManager-ref="transactionManager"/>
<bean id="jobOperator"
class="org.springframework.batch.core.launch.support.SimpleJobOperator"