ItemWriter

Reader์™€ Processor๋ฅผ ๊ฑฐ์ณ ์ฒ˜๋ฆฌ๋œ Item์„ Chunk ๋‹จ์œ„๋งŒํผ ์Œ“์€ ํ›„ ์ด๋ฅผ Writer์— ์ „๋‹ฌํ•˜๊ณ , ItemWriter๋Š” ๋ฐฐ์น˜์˜ ์ถœ๋ ฅ์„ ๋‹ด๋‹นํ•œ๋‹ค.

public interface ItemWriter<T> {
    void write(List<? extends T> var1) throws Exception;
}

ItemWriter์˜ write()๋Š” ์ธ์ž๋กœ Item List๋ฅผ ๋ฐ›๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. Spring Batch์—์„œ๋Š” ๋‹ค์–‘ํ•œ Output ํƒ€์ž…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก Writer๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.

Database Writer

  • JdbcBatchItemWriter

  • HibernateItemWriter

  • JpaItemWriter

๋‹ค์Œ 3๊ฐ€์ง€ Writer๊ฐ€ ์žˆ์œผ๋ฉฐ, Database์˜ ์˜์†์„ฑ๊ณผ ๊ด€๋ จํ•ด์„œ๋Š” ํ•ญ์ƒ Flush๋ฅผ ํ•ด์ค˜์•ผํ•œ๋‹ค. Writer๊ฐ€ ๋ฐ›์€ ๋ชจ๋“  Item์ด ์ฒ˜๋ฆฌ ๋œ ํ›„์— Spring Batch๋Š” ํ˜„์žฌ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•œ๋‹ค.

JdbcBatchItemWriter

ORM์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ๋Œ€๋ถ€๋ถ„ JdbcBatchItemWriter๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

jdbcwrite-flow

JdbcBatchItemWriter๋Š” JdbcTemplate์„ ์‚ฌ์šฉํ•˜๋ฉฐ, JDBC์˜ Batch ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ํ•œ๋ฒˆ์— DB๋กœ ์ „๋‹ฌํ•˜์—ฌ DB๋‚ด๋ถ€์—์„œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ•œ๋‹ค. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ํšŒ์ˆ˜๋ฅผ ์ตœ์†Œํ™”ํ•˜์—ฌ ์„ฑ๋Šฅํ–ฅ์ƒ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.

    public void write(final List<? extends T> items) throws Exception {
        if (!items.isEmpty()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Executing batch with " + items.size() + " items.");
            }

            int[] updateCounts;
            int value;
            if (!this.usingNamedParameters) {
                updateCounts = (int[])this.namedParameterJdbcTemplate.getJdbcOperations().execute(this.sql, new PreparedStatementCallback<int[]>() {
                    public int[] doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
                        Iterator var2 = items.iterator();

                        while(var2.hasNext()) {
                            T item = var2.next();
                            JdbcBatchItemWriter.this.itemPreparedStatementSetter.setValues(item, ps);
                            ps.addBatch();
                        }

                        return ps.executeBatch();
                    }
                });
            } else if (items.get(0) instanceof Map && this.itemSqlParameterSourceProvider == null) {
                updateCounts = this.namedParameterJdbcTemplate.batchUpdate(this.sql, (Map[])items.toArray(new Map[items.size()]));
            } else {
                SqlParameterSource[] batchArgs = new SqlParameterSource[items.size()];
                value = 0;

                Object item;
                for(Iterator var5 = items.iterator(); var5.hasNext(); batchArgs[value++] = this.itemSqlParameterSourceProvider.createSqlParameterSource(item)) {
                    item = var5.next();
                }

                updateCounts = this.namedParameterJdbcTemplate.batchUpdate(this.sql, batchArgs);
            }

            if (this.assertUpdates) {
                for(int i = 0; i < updateCounts.length; ++i) {
                    value = updateCounts[i];
                    if (value == 0) {
                        throw new EmptyResultDataAccessException("Item " + i + " of " + updateCounts.length + " did not update any rows: [" + items.get(i) + "]", 1);
                    }
                }
            }
        }

    }

์ด๋•Œ write()๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด SQL๋ฌธ์„ ํ•œ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ batchUpdate๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒญํฌ ๋‹จ์œ„๋กœ ์ผ๊ด„์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹คํ–‰ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹คํ–‰์„ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Property
Parameter Type
Default
์„ค๋ช…

assertUpdates

boolean

true

true์ด๋ฉด ๋ชจ๋“  ์•„์ดํ…œ์ด ์‚ฝ์ž…์ด๋‚˜ ์ˆ˜์ •๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•œ๋‹ค. ์ฆ‰, ์ ์–ด๋„ ํ•˜๋‚˜์˜ ํ•ญ๋ชฉ์ด ํ–‰์„ ์—…๋ฐ์ดํŠธ ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ(EmptyResultDataAccessException)๋ฅผ throwํ• ์ง€ ์„ค์ •ํ•œ๋‹ค.

dataSource

DataSource

null(ํ•„์ˆ˜)

ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ๊ณต

sql

String

null(ํ•„์ˆ˜)

๊ฐ ์•„์ดํ…œ๋‹น ์ˆ˜ํ–‰ํ•  SQL

itemPreparedStatementSetter

ItemPreparedStatementSetter

null

ํ‘œ์ค€ PreparedState๊ฐ€ ์ œ๊ณต๋œ๋‹ค๋ฉด(ํŒŒ๋ผ๋ฏธํ„ฐ ์œ„์น˜์— ?์‚ฌ์šฉ), ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ์ฑ„์›€

itemSqlParameterSourceProvider

ItemSqlParameterSourceProvider

null

์ œ๊ณต๋œ SQL์— ๋„ค์ž„๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด, ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’ ์ฑ„์›€

simpleJdbcTemplate

SimpleJdbcTemplate

null

SimpleJdbcOperations ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ฃผ์ž… ๊ฐ€๋Šฅ

afterPropertiesSet

๊ฐ๊ฐ Writer๋“ค์ด ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ํ•„์ˆ˜ ๊ฐ’๋“ค์ด ์ œ๋Œ€๋กœ ์„ธํŒ…๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ

JdbcBatchItemWriterBuilder

JdbcBatchItemWriterBuilder๋Š” ๋‹ค์Œ 3๊ฐ€์ง€ ์„ค์ • ๊ฐ’์„ ๊ฐ–๊ณ  ์žˆ๋‹ค.

Property
Parameter Type
Default
์„ค๋ช…

assertUpdates

boolean

true

true์ด๋ฉด ๋ชจ๋“  ์•„์ดํ…œ์ด ์‚ฝ์ž…์ด๋‚˜ ์ˆ˜์ •๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•œ๋‹ค. ์ฆ‰, ์ ์–ด๋„ ํ•˜๋‚˜์˜ ํ•ญ๋ชฉ์ด ํ–‰์„ ์—…๋ฐ์ดํŠธ ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ(EmptyResultDataAccessException)๋ฅผ throwํ• ์ง€ ์„ค์ •ํ•œ๋‹ค.

columnMapped

Key, Value ๊ธฐ๋ฐ˜์œผ๋กœ Insert SQL์˜ Values๋ฅผ ๋งคํ•‘ํ•œ๋‹ค.

beanMapped

POJO ๊ธฐ๋ฐ˜์œผ๋กœ Insert SQL์˜ Values๋ฅผ ๋งคํ•‘ํ•œ๋‹ค.

  • columnMapped

        @Bean // beanMapped์‹œ ํ•„์ˆ˜
        public JdbcBatchItemWriter<Pay> jdbcBatchItemWriter(){
            return new JdbcBatchItemWriterBuilder<Map<String, Object>>() // Map ์‚ฌ์šฉ
                    .columnMapped()
                    .dataSource(this.dataSource)
                    .sql("insert into pay2(amount, tx_name, tx_date_time) values (:amount, :txName, :txDateTime)")
                    .build();
        }
  • beanMapped

        @Bean // beanMapped์‹œ ํ•„์ˆ˜
        public JdbcBatchItemWriter<Pay> jdbcBatchItemWriter(){
            return new JdbcBatchItemWriterBuilder<Pay>()
                    .dataSource(dataSource)
                    .sql("insert into pay(amount, tx_name, tx_datetime) values (:amount, :txname, :txDateTime)")
                    .beanMapped()
                    .build();
        }

afterPropertiesSet

์ด ์™ธ์— afterPropertiesSet()๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ์ข‹๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” InitalizingBean ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉฐ, ItemWriter ๊ตฌํ˜„์ฒด๋“ค์€ ๋ชจ๋‘ InitializingBean ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค.

    public void afterPropertiesSet() {
        Assert.notNull(this.namedParameterJdbcTemplate, "A DataSource or a NamedParameterJdbcTemplate is required.");
        Assert.notNull(this.sql, "An SQL statement is required.");
        List<String> namedParameters = new ArrayList();
        this.parameterCount = JdbcParameterUtils.countParameterPlaceholders(this.sql, namedParameters);
        if (namedParameters.size() > 0) {
            if (this.parameterCount != namedParameters.size()) {
                throw new InvalidDataAccessApiUsageException("You can't use both named parameters and classic \"?\" placeholders: " + this.sql);
            }

            this.usingNamedParameters = true;
        }

        if (!this.usingNamedParameters) {
            Assert.notNull(this.itemPreparedStatementSetter, "Using SQL statement with '?' placeholders requires an ItemPreparedStatementSetter");
        }

    }

์ด ๋ฉ”์„œ๋“œ๋Š” ๊ฐ๊ฐ Writer๋“ค์ด ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ํ•„์ˆ˜ ๊ฐ’๋“ค์ด ์ œ๋Œ€๋กœ ์„ธํŒ…๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. Writer ์ƒ์„ฑ ํ›„ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์–ด๋А ๊ฐ’์ด ๋ˆ„๋ฝ๋˜์—ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์„œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.

    @Bean
    public JdbcBatchItemWriter<Pay> jdbcBatchItemWriter(){
        JdbcBatchItemWriter<Pay> jdbcBatchItemWriter = new JdbcBatchItemWriterBuilder<Pay>()
                                                          .dataSource(dataSource)
                                                          .sql("insert into pay(amount, tx_name, tx_date_time) values (:amount+1000, :txName, :txDateTime)")
                                                          .beanMapped()
                                                          .build();
        jdbcBatchItemWriter.afterPropertiesSet();
        return jdbcBatchItemWriter;
    }

HibernateItemWriter

  • org.springframework.batch.item.database.HibernateItemWriter

public class HibernateItemWriter<T> implements ItemWriter<T>, InitializingBean {
	@Override
	public void write(List<? extends T> items) {
		doWrite(sessionFactory, items);
		sessionFactory.getCurrentSession().flush();
		if(clearSession) {
			sessionFactory.getCurrentSession().clear();
		}
	}

	/**
	 * Do perform the actual write operation using Hibernate's API.
	 * This can be overridden in a subclass if necessary.
	 *
	 * @param sessionFactory Hibernate SessionFactory to be used
	 * @param items the list of items to use for the write
	 */
	protected void doWrite(SessionFactory sessionFactory, List<? extends T> items) {
		if (logger.isDebugEnabled()) {
			logger.debug("Writing to Hibernate with " + items.size()
					+ " items.");
		}

		Session currentSession = sessionFactory.getCurrentSession();

		if (!items.isEmpty()) {
			long saveOrUpdateCount = 0;
			for (T item : items) {
				if (!currentSession.contains(item)) {
					currentSession.saveOrUpdate(item);
					saveOrUpdateCount++;
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug(saveOrUpdateCount + " entities saved/updated.");
				logger.debug((items.size() - saveOrUpdateCount)
						+ " entities found in session.");
			}
		}
	}

HibernateItemWriter์—์„œ ๊ฐ ์•„์ดํ…œ์— ๋Œ€ํ•ด session.saveOrUpdate ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ, ๋ชจ๋“  ์•„์ดํ…œ์ด ์ €์žฅ๋˜๊ฑฐ๋‚˜ ์ˆ˜์ •๋˜๋ฉด flush ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ•œ๋ฒˆ์— ์‹คํ–‰ํ•œ๋‹ค.

์˜์กด์„ฑ ์ถ”๊ฐ€

compileOnly 'org.springframework.boot:spring-boot-starter-data-jpa'

ํ”„๋กœํผํ‹ฐ ์„ค์ •

spring:
  jpa:
    properties:
      hibernate:
        current_session_context_class: org.springframework.orm.hibernate5.SpringSessionContext

JPA ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

@Entity // ๋งคํ•‘ํ•  ๊ฐ์ฒด๊ฐ€ Entity์ž„์„ ๋‚˜ํƒ€๋ƒ„
@Table(name = "customer") // Entityrใ… ๋งคํ•‘๋˜๋Š” ํ…Œ์ด๋ธ” ์ง€์ •
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // pk
    private String firstName;
    private String middleInitial;
    private String lastName;
    private String address;
    private String city;
    private String state;
    private String zipCode;
}

Configurer ์ƒ์„ฑ

@Component
public class HibernateBatchConfigurer extends DefaultBatchConfigurer {

    private DataSource dataSource;
    private SessionFactory sessionFactory;
    private PlatformTransactionManager transactionManager;

    /**
     * Datasource connection๊ณผ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์„ธ์…˜ ์„ค์ •
     * @param dataSource
     * @param entityManagerFactory
     */
    public HibernateBatchConfigurer(DataSource dataSource,
                                    EntityManagerFactory entityManagerFactory) {
        super(dataSource);
        this.dataSource = dataSource;
        this.sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);

        // ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ํŠธ๋žœ์žญ์…˜ ์„ค์ •
        this.transactionManager = new HibernateTransactionManager(this.sessionFactory);
    }

    @Override
    public PlatformTransactionManager getTransactionManager() {
        return this.transactionManager;
    }
}

HibernateTransactionManager๋ฅผ ํŠธ๋žœ์žญ์…˜ ์œผ๋กœ ์„ค์ •ํ•ด์ค€๋‹ค.

    @Bean
    public HibernateItemWriter<Customer> hibernateItemWriter() {

        return new HibernateItemWriterBuilder<Customer>()
                .sessionFactory(entityManagerFactory.unwrap(SessionFactory.class))
                .build();
    }

JpaItemWriter

ORM์„ ์‚ฌ์šฉํ•  ๋•Œ, Writer์— ์ „๋‹ฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ Entity ํด๋ž˜์Šค์ธ ๊ฒฝ์šฐ JpaItemWriter๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. JpaItemWriter๋Š” JPA๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜์†์„ฑ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด EntityManager๋ฅผ ํ• ๋‹นํ•ด์ค˜์•ผํ•œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ spring-boot-starter-data-jpa๋ฅผ ์˜์กด์„ฑ์— ๋“ฑ๋กํ•˜๋ฉด EntityManager๊ฐ€ Bean์œผ๋กœ ์ž๋™ ์ƒ์„ฑ๋˜์–ด DI์ฝ”๋“œ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

compileOnly 'org.springframework.boot:spring-boot-starter-data-jpa'

afterPropertiesSet

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.entityManagerFactory, "An EntityManagerFactory is required");
    }

JpaItemWriter์˜ afterPropertiesSet()์—์„œ๋Š” EntityManagerFactory ๋งŒ ํ•„์ˆ˜ ๊ฐ’์œผ๋กœ ํ™•์ธํ•˜๊ณ  ์žˆ์–ด ์ฒดํฌํ•  ์š”์†Œ๊ฐ€ ์ ๋‹ค. ์ฆ‰, setEntityManger๋งŒ ํ•ด์ฃผ๋ฉด ๋ชจ๋“  ์„ค์ •์ด ๋๋‚œ๋‹ค.

		@Bean
    public JpaItemWriter<Pay> jpaCursorItemWriter() {

        JpaItemWriter<Pay> jpaItemWriter = new JpaItemWriter<>();
        jpaItemWriter.setEntityManagerFactory(entityManagerFactory);
        return jpaItemWriter;
    }

write()

    public void write(List<? extends T> items) {
        EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(this.entityManagerFactory);
        if (entityManager == null) {
            throw new DataAccessResourceFailureException("Unable to obtain a transactional EntityManager");
        } else {
            this.doWrite(entityManager, items);
            entityManager.flush();
        }
    }

    protected void doWrite(EntityManager entityManager, List<? extends T> items) {
        if (logger.isDebugEnabled()) {
            logger.debug("Writing to JPA with " + items.size() + " items.");
        }

        if (!items.isEmpty()) {
            long addedToContextCount = 0L;
            Iterator var5 = items.iterator();

            while(var5.hasNext()) {
                T item = var5.next();
                if (!entityManager.contains(item)) {
                    if (this.usePersist) {
                        entityManager.persist(item);
                    } else {
                        entityManager.merge(item);
                    }

                    ++addedToContextCount;
                }
            }

            if (logger.isDebugEnabled()) {
                logger.debug(addedToContextCount + " entities " + (this.usePersist ? " persisted." : "merged."));
                logger.debug((long)items.size() - addedToContextCount + " entities found in persistence context.");
            }
        }

    }

JpaItemWriter์˜ doWrite()๋ฅผ ๋ณด๋ฉด ๋„˜์–ด์˜จ item ๊ทธ๋Œ€๋กœ entityManager.merge(item)๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ํ…Œ์ด๋ธ”์— ๋ฐ”๋กœ ๋ฐ˜์˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, JpaItemWriter๋Š” Entity ํด๋ž˜์Šค๋ฅผ ์ œ๋„ค๋ฆญ ํƒ€์ž…์œผ๋กœ ๋ฐ›์•„์•ผ๋งŒ ํ•œ๋‹ค.

MyBatisBatchItemWriter

Step์—์„œ ์ •์˜ํ•œ Chunk Size(FetchSize)๋งŒํผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋ ค๋ฉด executorType์„ BATCH๋กœ ์„ค์ •ํ•ด์ค˜์•ผํ•œ๋‹ค.

mybatis:
	config-location: classpath:mybatis/mybatis-config.xml
	mapper-locations: classpath*:mybatis/**/*.sql
	executorType: BATCH

๊ทธ ๋‹ค์Œ ์ˆ˜ํ–‰ํ•  ์ฟผ๋ฆฌ๋ฅผ mapper์— ์ž‘์„ฑํ•ด์ฃผ๊ณ  ์ˆ˜ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="spring.batch.practice.dao.customerMapper">

    <select id="insertCustomer" parameterType="spring.batch.practice.domain.Customer">
		INSERT INTO CUSTOMER(FIRST_NAME, MIDDLE_INITIAL, LAST_NAME, ADDRESS, CITY, STATE, ZIP_CODE)
    VALUES (#{firstName}, #{middleInitial}, #{lastName}, #{address}, #{city}, #{state}, #{zipCode})
		</select>
</mapper>
@Bean
public MyBatisBatchItemWriter<Customer> testWriter(SqlSessionFactory sqlSessionFactory) {
    return new MyBatisBatchItemWriterBuilder<Customer>()
    .sqlSessionFactory(sqlSessionFactory)
    .statementId("spring.batch.practice.dao.customerMapper.insertCustomer")
    .build();
}

RepositoryItemWriter

์“ฐ๊ธฐ ์ž‘์—… ์ˆ˜ํ–‰์‹œ์—๋Š” ํŽ˜์ด์ง•์ด๋‚˜ ์ •๋ ฌ์ด ํ•„์š”์—†์œผ๋ฏ€๋กœ, CrudRepository๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
    @Bean
    public RepositoryItemWriter<Customer> repositoryItemWriter() {

        return new RepositoryItemWriterBuilder<Customer>()
                .repository(customerRepository)
                .methodName("save")
                .build();
    }

์œ„์—์„œ ๊ตฌํ˜„ํ•œ repository๋ฅผ ์„ค์ •ํ•ด์ฃผ๊ณ , ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ๋ช…๋งŒ ์ง€์ •ํ•ด์ฃผ๋ฉด๋œ๋‹ค.

Custom ItemWriter

Reader์™€๋Š” ๋‹ค๋ฅด๊ฒŒ Writer์˜ ๊ฒฝ์šฐ customํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

  • Reader์—์„œ ์ฝ์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ RestTemplate์œผ๋กœ ์™ธ๋ถ€ API๋ฅผ ์ „๋‹ฌํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ

  • ์ž„์‹œ ์ €์žฅ์„ ํ•˜๊ณ  ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด singleton ๊ฐ์ฒด์— ๊ฐ’์„ ๋„ฃ์–ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ

  • ์—ฌ๋Ÿฌ Entity๋ฅผ ๋™์‹œ์— ์ €์žฅํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ์ƒํ™ฉ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ItemWriter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

  • java7 ์ดํ•˜

      @Bean
        public ItemWriter<Pay> customItemWriter() {
            return new ItemWriter<Pay>() {
                @Override
                public void write(List<? extends Pay> items) throws Exception {
                    for (Pay item : items) {
                        System.out.println(item);
                    }
                }
            };
        }
  • java8 ์ด์ƒ(ItemWriter์˜ ์ถ”์ƒ๋ฉ”์„œ๋“œ๊ฐ€ write() ํ•œ๊ฐœ ์ด๋ฏ€๋กœ ๋žŒ๋‹ค์‹ ์‚ฌ์šฉ ๊ฐ€๋Šฅ)

     @Bean
       public ItemWriter<Pay> customItemWriter() {
           return items -> {
               for (Pay item : items) {
                   System.out.println(item);
               }
           };
       }

๋‹ค์Œ๊ณผ ๊ฐ™์ด write()ํ•จ์ˆ˜๋ฅผ @Overrideํ•˜๋ฉด ๊ตฌํ˜„์ฒด ์ƒ์„ฑ์€ ๋๋‚œ๋‹ค.

์ฐธ๊ณ 

Last updated

Was this helpful?