View Javadoc
1   /*
2    * Copyright 2010-2025 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.mybatis.spring.batch;
17  
18  import static org.springframework.util.Assert.notNull;
19  import static org.springframework.util.ClassUtils.getShortName;
20  
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.Optional;
25  import java.util.function.Supplier;
26  
27  import org.apache.ibatis.cursor.Cursor;
28  import org.apache.ibatis.session.ExecutorType;
29  import org.apache.ibatis.session.SqlSession;
30  import org.apache.ibatis.session.SqlSessionFactory;
31  import org.springframework.batch.infrastructure.item.support.AbstractItemCountingItemStreamItemReader;
32  import org.springframework.beans.factory.InitializingBean;
33  
34  /**
35   * {@code ItemReader} that uses MyBatis Cursor to read data.
36   *
37   * @author Guillaume Darmont / guillaume@dropinocean.com
38   *
39   * @param <T>
40   *          the generic type
41   */
42  public class MyBatisCursorItemReader<T> extends AbstractItemCountingItemStreamItemReader<T>
43      implements InitializingBean {
44  
45    private String queryId;
46  
47    private SqlSessionFactory sqlSessionFactory;
48    private SqlSession sqlSession;
49  
50    private Map<String, Object> parameterValues;
51    private Supplier<Map<String, Object>> parameterValuesSupplier;
52  
53    private Cursor<T> cursor;
54    private Iterator<T> cursorIterator;
55  
56    /**
57     * Instantiates a new my batis cursor item reader.
58     */
59    public MyBatisCursorItemReader() {
60      setName(getShortName(MyBatisCursorItemReader.class));
61    }
62  
63    @Override
64    protected T doRead() throws Exception {
65      T next = null;
66      if (cursorIterator.hasNext()) {
67        next = cursorIterator.next();
68      }
69      return next;
70    }
71  
72    @Override
73    protected void doOpen() throws Exception {
74      Map<String, Object> parameters = new HashMap<>();
75      if (parameterValues != null) {
76        parameters.putAll(parameterValues);
77      }
78  
79      Optional.ofNullable(parameterValuesSupplier).map(Supplier::get).ifPresent(parameters::putAll);
80  
81      sqlSession = sqlSessionFactory.openSession(ExecutorType.SIMPLE);
82      cursor = sqlSession.selectCursor(queryId, parameters);
83      cursorIterator = cursor.iterator();
84    }
85  
86    @Override
87    protected void doClose() throws Exception {
88      if (cursor != null) {
89        cursor.close();
90      }
91      if (sqlSession != null) {
92        sqlSession.close();
93      }
94      cursorIterator = null;
95    }
96  
97    /**
98     * Check mandatory properties.
99     *
100    * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
101    */
102   @Override
103   public void afterPropertiesSet() throws Exception {
104     notNull(sqlSessionFactory, "A SqlSessionFactory is required.");
105     notNull(queryId, "A queryId is required.");
106   }
107 
108   /**
109    * Public setter for {@link SqlSessionFactory} for injection purposes.
110    *
111    * @param sqlSessionFactory
112    *          a factory object for the {@link SqlSession}.
113    */
114   public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
115     this.sqlSessionFactory = sqlSessionFactory;
116   }
117 
118   /**
119    * Public setter for the statement id identifying the statement in the SqlMap configuration file.
120    *
121    * @param queryId
122    *          the id for the statement
123    */
124   public void setQueryId(String queryId) {
125     this.queryId = queryId;
126   }
127 
128   /**
129    * The parameter values to be used for the query execution.
130    *
131    * @param parameterValues
132    *          the values keyed by the parameter named used in the query string.
133    */
134   public void setParameterValues(Map<String, Object> parameterValues) {
135     this.parameterValues = parameterValues;
136   }
137 
138   /**
139    * The parameter supplier used to get parameter values for the query execution.
140    *
141    * @param parameterValuesSupplier
142    *          the supplier used to get values keyed by the parameter named used in the query string.
143    *
144    * @since 2.1.0
145    */
146   public void setParameterValuesSupplier(Supplier<Map<String, Object>> parameterValuesSupplier) {
147     this.parameterValuesSupplier = parameterValuesSupplier;
148   }
149 }