Возвращает несколько элементов из spring партии ItemProcessor
Я пишу пакетное задание spring, и на одном из моих шагов у меня есть следующий код для процессора:
@Component
public class SubscriberProcessor implements ItemProcessor<NewsletterSubscriber, Account>, InitializingBean {
@Autowired
private AccountService service;
@Override public Account process(NewsletterSubscriber item) throws Exception {
if (!Strings.isNullOrEmpty(item.getId())) {
return service.getAccount(item.getId());
}
// search with email address
List<Account> accounts = service.findByEmail(item.getEmail());
checkState(accounts.size() <= 1, "Found more than one account with email %s", item.getEmail());
return accounts.isEmpty() ? null : accounts.get(0);
}
@Override public void afterPropertiesSet() throws Exception {
Assert.notNull(service, "account service must be set");
}
}
Вышеприведенный код работает, но я обнаружил, что есть некоторые случаи ребер, где допускается более одного Account
за NewsletterSubscriber
. Поэтому мне нужно удалить проверку состояния и передать более чем одному Account
писателю элемента.
Одно из найденных решений - изменить как ItemProcessor
, так и ItemWriter
для работы с типом List<Account>
вместо Account
, но это имеет два недостатка:
- Код и тесты более уродливы и сложнее писать и поддерживать из-за вложенных списков в писателя
- Самое важное более одного объекта
Account
может быть записано в той же транзакции, потому что список, предоставленный писателю, может содержать несколько учетных записей, и я хотел бы избежать этого.
Есть ли способ, может быть, использовать прослушиватель или заменить какой-либо внутренний компонент, используемый пакетом spring, чтобы избежать списков в процессоре?
Update
Я открыл проблему на spring Jira для этой проблемы.
Я изучаю isComplete и getAdjustedOutputs методы в FaultTolerantChunkProcessor
, которые помечены как точки расширения в SimpleChunkProcessor
, чтобы увидеть, могу ли я каким-либо образом использовать их для достижения моей цели.
Любые подсказки приветствуются.
Ответы
Ответ 1
Элемент Процессор принимает одну вещь и возвращает список
MyItemProcessor implements ItemProcessor<SingleThing,List<ExtractedThingFromSingleThing>> {
public List<ExtractedThingFromSingleThing> process(SingleThing thing) {
//parse and convert to list
}
}
Оберните нисходящего автора, чтобы утюжить вещи. Таким образом, материал ниже по течению от этого автора не должен работать со списками.
@StepScope
public class ItemListWriter<T> implements ItemWriter<List<T>> {
private ItemWriter<T> wrapped;
public ItemListWriter(ItemWriter<T> wrapped) {
this.wrapped = wrapped;
}
@Override
public void write(List<? extends List<T>> items) throws Exception {
for (List<T> subList : items) {
wrapped.write(subList);
}
}
}
Ответ 2
Невозможно вернуть более одного элемента для каждого звонка в пакет ItemProcessor
в Spring, не попадая далеко в сорняки. Если вы действительно хотите узнать, где выходят отношения между ItemProcessor
и ItemWriter
(не рекомендуется), посмотрите на реализации интерфейса ChunkProcessor
. В то время как простой случай (SimpleChunkProcessor
) не так уж плох, если вы используете какую-либо отказоустойчивую логику (пропустите/повторите попытку через FaultTolerantChunkProcessor
), она становится очень неудобно быстрой.
Более простой вариант заключается в том, чтобы переместить эту логику в ItemReader
, которая делает это обогащение, прежде чем возвращать элемент. Обменивайте все ItemReader
, которые вы используете в пользовательской реализации ItemReader
, которая выполняет поиск службы перед возвратом элемента. В этом случае вместо возврата NewsletterSubscriber
из читателя вы должны вернуть Account
на основе предыдущей информации.
Ответ 3
Вместо возврата возвращаемой учетной записи создайте AccountWrapper или Collection. Писатель, очевидно, должен учитывать это:)
Ответ 4
Вы можете сделать трансформер для преобразования вашего Pojo (объект Pojo из файла) в вашу сущность, сделав следующий код:
public class Intializer {
public static LGInfo initializeEntity() throws Exception {
Constructor<LGInfo> constr1 = LGInfo.class.getConstructor();
LGInfo info = constr1.newInstance();
return info;
}
}
И в вашем пункте Процессор
public class LgItemProcessor<LgBulkLine, LGInfo> implements ItemProcessor<LgBulkLine, LGInfo> {
private static final Log log = LogFactory.getLog(LgItemProcessor.class);
@SuppressWarnings("unchecked")
@Override
public LGInfo process(LgBulkLine item) throws Exception {
log.info(item);
return (LGInfo) Intializer.initializeEntity();
}
}