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.builder;
17  
18  import java.util.Arrays;
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.ibatis.cursor.Cursor;
25  import org.apache.ibatis.session.ExecutorType;
26  import org.apache.ibatis.session.SqlSession;
27  import org.apache.ibatis.session.SqlSessionFactory;
28  import org.assertj.core.api.Assertions;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.Test;
31  import org.junit.jupiter.api.extension.ExtendWith;
32  import org.mockito.Mock;
33  import org.mockito.Mockito;
34  import org.mockito.junit.jupiter.MockitoExtension;
35  import org.springframework.batch.infrastructure.item.ExecutionContext;
36  
37  /**
38   * Tests for {@link MyBatisCursorItemReaderBuilder}.
39   *
40   * @since 2.0.0
41   *
42   * @author Kazuki Shimizu
43   */
44  @ExtendWith(MockitoExtension.class)
45  class MyBatisCursorItemReaderBuilderTest {
46  
47    @Mock
48    private SqlSessionFactory sqlSessionFactory;
49  
50    @Mock
51    private SqlSession sqlSession;
52  
53    @Mock
54    private Cursor<Object> cursor;
55  
56    @BeforeEach
57    void setUp() {
58      Mockito.when(this.sqlSessionFactory.openSession(ExecutorType.SIMPLE)).thenReturn(this.sqlSession);
59      Mockito.when(this.cursor.iterator()).thenReturn(getFoos().iterator());
60      Map<String, Object> parameters = new HashMap<>();
61      parameters.put("id", 1);
62      parameters.put("name", "Doe");
63      Mockito.when(this.sqlSession.selectCursor("selectFoo", parameters)).thenReturn(this.cursor);
64    }
65  
66    @Test
67    void testConfiguration() throws Exception {
68  
69      // @formatter:off
70          var itemReader = new MyBatisCursorItemReaderBuilder<Foo>()
71                  .sqlSessionFactory(this.sqlSessionFactory)
72                  .queryId("selectFoo")
73                  .parameterValues(Collections.singletonMap("id", 1))
74                  .parameterValuesSupplier(() -> Collections.singletonMap("name", "Doe"))
75                  .build();
76          // @formatter:on
77      itemReader.afterPropertiesSet();
78  
79      var executionContext = new ExecutionContext();
80      itemReader.open(executionContext);
81  
82      Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo1");
83      Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo2");
84      Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo3");
85  
86      itemReader.update(executionContext);
87      Assertions.assertThat(executionContext.getInt("MyBatisCursorItemReader.read.count")).isEqualTo(3);
88      Assertions.assertThat(executionContext.containsKey("MyBatisCursorItemReader.read.count.max")).isFalse();
89  
90      Assertions.assertThat(itemReader.read()).isNull();
91    }
92  
93    @Test
94    void testConfigurationSaveStateIsFalse() throws Exception {
95  
96      // @formatter:off
97          var itemReader = new MyBatisCursorItemReaderBuilder<Foo>()
98                  .sqlSessionFactory(this.sqlSessionFactory)
99                  .queryId("selectFoo")
100                 .parameterValues(Collections.singletonMap("id", 1))
101                 .parameterValuesSupplier(() -> Collections.singletonMap("name", "Doe"))
102                 .saveState(false)
103                 .build();
104         // @formatter:on
105     itemReader.afterPropertiesSet();
106 
107     var executionContext = new ExecutionContext();
108     itemReader.open(executionContext);
109 
110     Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo1");
111     Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo2");
112     Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo3");
113 
114     itemReader.update(executionContext);
115     Assertions.assertThat(executionContext.isEmpty()).isTrue();
116 
117   }
118 
119   @Test
120   void testConfigurationMaxItemCount() throws Exception {
121 
122     // @formatter:off
123         var itemReader = new MyBatisCursorItemReaderBuilder<Foo>()
124                 .sqlSessionFactory(this.sqlSessionFactory)
125                 .queryId("selectFoo")
126                 .parameterValues(Collections.singletonMap("id", 1))
127                 .parameterValuesSupplier(() -> Collections.singletonMap("name", "Doe"))
128                 .maxItemCount(2)
129                 .build();
130         // @formatter:on
131     itemReader.afterPropertiesSet();
132 
133     var executionContext = new ExecutionContext();
134     itemReader.open(executionContext);
135 
136     Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo1");
137     Assertions.assertThat(itemReader.read()).extracting(Foo::getName).isEqualTo("foo2");
138 
139     itemReader.update(executionContext);
140     Assertions.assertThat(executionContext.getInt("MyBatisCursorItemReader.read.count.max")).isEqualTo(2);
141 
142     Assertions.assertThat(itemReader.read()).isNull();
143   }
144 
145   private List<Object> getFoos() {
146     return Arrays.asList(new Foo("foo1"), new Foo("foo2"), new Foo("foo3"));
147   }
148 
149   private static class Foo {
150     private final String name;
151 
152     Foo(String name) {
153       this.name = name;
154     }
155 
156     public String getName() {
157       return this.name;
158     }
159   }
160 
161 }