001/*
002 * Copyright 2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.springframework.batch.item.database.builder;
017
018import java.math.BigInteger;
019import java.util.Map;
020import javax.sql.DataSource;
021
022import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
023import org.springframework.batch.item.database.ItemPreparedStatementSetter;
024import org.springframework.batch.item.database.ItemSqlParameterSourceProvider;
025import org.springframework.batch.item.database.JdbcBatchItemWriter;
026import org.springframework.batch.item.database.support.ColumnMapItemPreparedStatementSetter;
027import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
028import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
029import org.springframework.util.Assert;
030
031/**
032 * A builder implementation for the {@link JdbcBatchItemWriter}.
033 *
034 * @author Michael Minella
035 * @since 4.0
036 * @see JdbcBatchItemWriter
037 */
038public class JdbcBatchItemWriterBuilder<T> {
039
040        private boolean assertUpdates = true;
041
042        private String sql;
043
044        private ItemPreparedStatementSetter<T> itemPreparedStatementSetter;
045
046        private ItemSqlParameterSourceProvider<T> itemSqlParameterSourceProvider;
047
048        private DataSource dataSource;
049
050        private NamedParameterJdbcOperations namedParameterJdbcTemplate;
051
052        private BigInteger mapped = new BigInteger("0");
053
054        /**
055         * Configure the {@link DataSource} to be used.
056         *
057         * @param dataSource the DataSource
058         * @return The current instance of the builder for chaining.
059         * @see JdbcBatchItemWriter#setDataSource(DataSource)
060         */
061        public JdbcBatchItemWriterBuilder<T> dataSource(DataSource dataSource) {
062                this.dataSource = dataSource;
063
064                return this;
065        }
066
067        /**
068         * If set to true, confirms that every insert results in the update of at least one
069         * row in the database.  Defaults to true.
070         *
071         * @param assertUpdates boolean indicator
072         * @return The current instance of the builder for chaining
073         * @see JdbcBatchItemWriter#setAssertUpdates(boolean)
074         */
075        public JdbcBatchItemWriterBuilder<T> assertUpdates(boolean assertUpdates) {
076                this.assertUpdates = assertUpdates;
077
078                return this;
079        }
080
081        /**
082         * Set the SQL statement to be used for each item's updates.  This is a required
083         * field.
084         *
085         * @param sql SQL string
086         * @return The current instance of the builder for chaining
087         * @see JdbcBatchItemWriter#setSql(String)
088         */
089        public JdbcBatchItemWriterBuilder<T> sql(String sql) {
090                this.sql = sql;
091
092                return this;
093        }
094
095        /**
096         * Configures a {@link ItemPreparedStatementSetter} for use by the writer.  This
097         * should only be used if {@link #columnMapped()} isn't called.
098         *
099         * @param itemPreparedStatementSetter The {@link ItemPreparedStatementSetter}
100         * @return The current instance of the builder for chaining
101         * @see JdbcBatchItemWriter#setItemPreparedStatementSetter(ItemPreparedStatementSetter)
102         */
103        public JdbcBatchItemWriterBuilder<T> itemPreparedStatementSetter(ItemPreparedStatementSetter<T> itemPreparedStatementSetter) {
104                this.itemPreparedStatementSetter = itemPreparedStatementSetter;
105
106                return this;
107        }
108
109        /**
110         * Configures a {@link ItemSqlParameterSourceProvider} for use by the writer.  This
111         * should only be used if {@link #beanMapped()} isn't called.
112         *
113         * @param itemSqlParameterSourceProvider The {@link ItemSqlParameterSourceProvider}
114         * @return The current instance of the builder for chaining
115         * @see JdbcBatchItemWriter#setItemSqlParameterSourceProvider(ItemSqlParameterSourceProvider)
116         */
117        public JdbcBatchItemWriterBuilder<T> itemSqlParameterSourceProvider(ItemSqlParameterSourceProvider<T> itemSqlParameterSourceProvider) {
118                this.itemSqlParameterSourceProvider = itemSqlParameterSourceProvider;
119
120                return this;
121        }
122
123        /**
124         * The {@link NamedParameterJdbcOperations} instance to use.  If one isn't provided,
125         * a {@link DataSource} is required.
126         *
127         * @param namedParameterJdbcOperations The template
128         * @return The current instance of the builder for chaining
129         */
130        public JdbcBatchItemWriterBuilder<T> namedParametersJdbcTemplate(NamedParameterJdbcOperations namedParameterJdbcOperations) {
131                this.namedParameterJdbcTemplate = namedParameterJdbcOperations;
132
133                return this;
134        }
135
136        /**
137         * Creates a {@link ColumnMapItemPreparedStatementSetter} to be used as your
138         * {@link ItemPreparedStatementSetter}.
139         *
140         * NOTE: The item type for this {@link org.springframework.batch.item.ItemWriter} must
141         * be castable to <code>Map&lt;String,Object&gt;&gt;</code>.
142         *
143         * @return The current instance of the builder for chaining
144         * @see ColumnMapItemPreparedStatementSetter
145         */
146        public JdbcBatchItemWriterBuilder<T> columnMapped() {
147                this.mapped = this.mapped.setBit(0);
148
149                return this;
150        }
151
152        /**
153         * Creates a {@link BeanPropertyItemSqlParameterSourceProvider} to be used as your
154         * {@link ItemSqlParameterSourceProvider}.
155         *
156         * @return The current instance of the builder for chaining
157         * @see BeanPropertyItemSqlParameterSourceProvider
158         */
159        public JdbcBatchItemWriterBuilder<T> beanMapped() {
160                this.mapped = this.mapped.setBit(1);
161
162                return this;
163        }
164
165        /**
166         * Validates configuration and builds the {@link JdbcBatchItemWriter}.
167         *
168         * @return a {@link JdbcBatchItemWriter}
169         */
170        @SuppressWarnings("unchecked")
171        public JdbcBatchItemWriter<T> build() {
172                Assert.state(this.dataSource != null || this.namedParameterJdbcTemplate != null,
173                                "Either a DataSource or a NamedParameterJdbcTemplate is required");
174
175                Assert.notNull(this.sql, "A SQL statement is required");
176                int mappedValue = this.mapped.intValue();
177                Assert.state(mappedValue != 3,
178                                "Either an item can be mapped via db column or via bean spec, can't be both");
179
180                JdbcBatchItemWriter<T> writer = new JdbcBatchItemWriter<>();
181                writer.setSql(this.sql);
182                writer.setAssertUpdates(this.assertUpdates);
183                writer.setItemSqlParameterSourceProvider(this.itemSqlParameterSourceProvider);
184                writer.setItemPreparedStatementSetter(this.itemPreparedStatementSetter);
185
186                if(mappedValue == 1) {
187                        ((JdbcBatchItemWriter<Map<String,Object>>)writer).setItemPreparedStatementSetter(new ColumnMapItemPreparedStatementSetter());
188                } else if(mappedValue == 2) {
189                        writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
190                }
191
192                if(this.dataSource != null) {
193                        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(this.dataSource);
194                }
195
196                writer.setJdbcTemplate(this.namedParameterJdbcTemplate);
197
198                return writer;
199        }
200}