Spring batch Custom itemWriter с механизмом вставки или обновления

У меня есть Spring Batch с базовым чанком:

  1. Прочитайте CSV-файл.
  2. Обработайте его (перекодируйте одно закодированное значение в другое).
  3. Запишите результат в базу данных.

Проблема

В некоторых случаях я хочу вставить или обновить механизм в своем пользовательском файле ItemWriter.

Пример: я могу получить эти 2 строки на моем CSV

C006;Test;OK;01/01/1970;1
C006;Test;OK;01/01/1970;5

Вы можете понять, что они очень похожи, за исключением последнего столбца, стратегия будет такой:

  1. Проверьте базу данных, если у меня есть сущность "как так"
  2. если true, обновите значение последним полученным элементом (в нашем случае вторая строка со значением 5 в последнем столбце)

Что я уже сделал?

В свой объектный компонент я добавил аннотацию Unique Constraint следующим образом:

@Table(name = "indicator",
    uniqueConstraints = { @UniqueConstraint(columnNames = 
                                        { "reference", "type", "status", "date" }) })

Теперь я уверен, что не могу сохранить объект с данными того же столбца. Затем я создал пользовательское исключение ItemWriter и попытался перехватить ConstraintViolationException, но это не сработало, я всегда получаю другое исключение, даже когда пробовал родительское.

Итак, у вас есть какие-либо идеи или другой способ сделать это?

Я думал об использовании функций слияния JPA? что ты думаешь об этом?

Мой пользовательский ItemWriter

@Component
public class ImportItemWriter implements ItemWriter<Indicator>{

    @Autowired
    protected IndicatorDao indicatorDao;

    public void write(List<? extends Indicator> items) throws Exception {

        for (Indicator item : items) {
            Indicator indicator = new Indicator();

            indicator.setReference(item.getReference());
            indicator.setType(item.getType());
            indicator.setStatus(item.getStatus());
            indicator.setDueDate(item.getDueDate());

            indicator.setValue(item.getValue());

            try {
                indicatorDao.persist(indicator);
            } catch (ConstraintViolationException e) {
                // TODO: handle exception
            }       
        }
    }
}

Обновление Проблема решена Идея использования Composite PKey интересна, но я не могу ее использовать, потому что мне нужно создать композицию с 9 ключами, не справедливо с точки зрения производительности. Я решил добавить функцию в свой DAO (isDuplicated) и в своем пользовательском ItemWriter просто провел простой тест:

if `isDuplicated()` then `updateEntity()` else `insertNew()`

person TheCyberXP    schedule 18.09.2014    source источник
comment
Думали ли вы об использовании этого напрямую? .. реализация выполняет entityManger.merge(..) .. если у вашей сущности есть составной pkey, вы можете решить таким образом.   -  person Xstian    schedule 18.09.2014
comment
Ага, я раньше использовал JpaItemWriter, но не сработало, я предполагаю, что ловушка слияния только с первичным ключом и в моем случае это меня не волнует, у меня есть конкретная цель, проверка одновременно 4 атрибутов   -  person TheCyberXP    schedule 18.09.2014
comment
Вы можете создать составной первичный ключ с вашими 4 атрибутами :) .. но если вы не можете изменить свою сущность, это то же самое, что и ваш ImportItemWriter. ;) см. эту ссылку, чтобы создать составной первичный ключ   -  person Xstian    schedule 18.09.2014
comment
Интересно, попробую. Спасибо   -  person TheCyberXP    schedule 18.09.2014
comment
вы решили эту проблему? если да, то как вы ее решили?   -  person Xstian    schedule 13.10.2014


Ответы (2)


Одним из вариантов было бы использовать ClassifierCompositeItemWriter. Classifier будет использоваться для определения того, следует ли выполнять вставку или обновление. Затем вы настроите двух делегатов, одного для вставок, другого для обновлений.

person Michael Minella    schedule 18.09.2014
comment
Это не решило проблему определения того, нужно ли мне вставлять или обновлять - person TheCyberXP; 18.09.2014
comment
Какая часть ваших потребностей не удовлетворяется этим решением? - person Michael Minella; 18.09.2014
comment
Идея использования классификатора совсем не плоха, но в моем случае у меня другая проблема. По сути, я хотел бы найти механизм, который позволит мне определить, нужно ли мне обновлять или вставлять элемент. Потом вставить или обновить не проблема, но сначала надо дело поймать. Спасибо ;) - person TheCyberXP; 19.09.2014

Обобщите приведенные выше комментарии.

Думали ли вы об использовании этого напрямую? .. реализация выполняет entityManger.merge(..) .. если у вашей сущности есть составной pkey, вы можете решить таким образом.

    <job id="writeProductsJob" xmlns="http://www.springframework.org/schema/batch">
        <step id="readWrite">
            <tasklet>
                <chunk reader="productItemReader" writer="productItemWriter" commit-interval="3" />
            </tasklet>
        </step>
    </job>

    <bean id="productItemWriter" class="org.springframework.batch.item.database.JpaItemWriter">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

С помощью @EmbeddedId вы можете создать составной первичный ключ для объединения сущности. См. эту ссылку.

Объект

@Entity
@Table(name = "CAR")
public class Car {

    @EmbeddedId
    private CarPK carPK;

    @Column
    private String name;
}

Составной первичный ключ

@Embeddable
public class CarPK implements Serializable {

    @Column
    private String chassisSerialNumber;

    @Column
    private String engineSerialNumber;

    public CarPK(){
        // Your class must have a no-arq constructor
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof CarPK){
            CarPK carPk = (CarPK) obj;

            if(!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){
                return false;
            }

            if(!carPk.getEngineSerialNumber().equals(engineSerialNumber)){
                return false;
            }

            return true;
        }

        return false;
    }

    @Override
    public int hashCode() {
        return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode();
    }

    //setter and getter
}
person Xstian    schedule 18.09.2014
comment
Отличная идея, я не знал, что раньше было что-то вроде составного Pkey! но перед тестированием, с концептуальной точки зрения, хорошо ли создать сущность, содержащую составной pkey с 9 аргументами? - person TheCyberXP; 19.09.2014
comment
Я думаю, что концептуально это правильно, если набор каждого поля должен быть уникальным в вашей базе данных. Это хороший способ еще и потому, что этим занимается ORM. JPA не оказывает существенного влияния на производительность независимо от того, является ли это составным ключом или нет. см. эту ссылку - person Xstian; 19.09.2014