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;
17  
18  import static org.assertj.core.api.Assertions.assertThat;
19  import static org.junit.jupiter.api.Assertions.assertThrows;
20  import static org.junit.jupiter.api.Assertions.fail;
21  
22  import java.sql.SQLException;
23  
24  import org.apache.ibatis.session.ExecutorType;
25  import org.apache.ibatis.session.SqlSession;
26  import org.junit.jupiter.api.AfterEach;
27  import org.junit.jupiter.api.BeforeAll;
28  import org.junit.jupiter.api.Test;
29  import org.springframework.dao.DataAccessException;
30  import org.springframework.jdbc.datasource.DataSourceTransactionManager;
31  import org.springframework.transaction.TransactionStatus;
32  import org.springframework.transaction.support.DefaultTransactionDefinition;
33  import org.springframework.transaction.support.TransactionSynchronizationManager;
34  
35  // tests basic usage and implementation only
36  // MapperFactoryBeanTest handles testing the transactional functions in SqlSessionTemplate
37  // Method is public as extended by other test classes outside this package
38  public class SqlSessionTemplateTest extends AbstractMyBatisSpringTest {
39  
40    private static SqlSession sqlSessionTemplate;
41  
42    @BeforeAll
43    static void setupSqlTemplate() {
44      sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
45    }
46  
47    @AfterEach
48    void tearDown() {
49      try {
50        connection.close();
51      } catch (SQLException ignored) {
52      }
53    }
54  
55    @Test
56    void testGetConnection() throws java.sql.SQLException {
57      var conn = sqlSessionTemplate.getConnection();
58  
59      // outside of an explicit tx, getConnection() will start a tx, get an open connection then
60      // end the tx, which closes the connection
61      assertThat(conn.isClosed()).isTrue();
62    }
63  
64    @Test
65    void testGetConnectionInTx() throws java.sql.SQLException {
66      TransactionStatus status = null;
67  
68      try {
69        status = txManager.getTransaction(new DefaultTransactionDefinition());
70  
71        var conn = sqlSessionTemplate.getConnection();
72  
73        assertThat(conn.isClosed()).isFalse();
74  
75      } finally {
76        // rollback required to close connection
77        txManager.rollback(status);
78      }
79    }
80  
81    @Test
82    void testCommit() {
83      assertThrows(UnsupportedOperationException.class, sqlSessionTemplate::commit);
84    }
85  
86    @Test
87    void testClose() {
88      assertThrows(UnsupportedOperationException.class, sqlSessionTemplate::close);
89    }
90  
91    @Test
92    void testRollback() {
93      assertThrows(UnsupportedOperationException.class, sqlSessionTemplate::rollback);
94    }
95  
96    @Test
97    void testExecutorType() {
98      // Do not close this, spring will close it
99      var template = new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
100     assertThat(template.getExecutorType()).isEqualTo(ExecutorType.BATCH);
101 
102     var manager = new DataSourceTransactionManager(dataSource);
103 
104     TransactionStatus status = null;
105 
106     try {
107       status = manager.getTransaction(new DefaultTransactionDefinition());
108 
109       // will synchronize the template with the current tx
110       template.getConnection();
111 
112       var holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
113 
114       assertThat(holder.getExecutorType()).isEqualTo(ExecutorType.BATCH);
115     } finally {
116       // rollback required to close connection
117       txManager.rollback(status);
118     }
119   }
120 
121   @Test
122   void testExceptionTranslationShouldThrowMyBatisSystemException() throws SQLException {
123     try {
124       sqlSessionTemplate.selectOne("undefined");
125       fail("exception not thrown when expected");
126     } catch (MyBatisSystemException mbse) {
127       // success
128     } catch (Throwable t) {
129       fail("SqlSessionTemplate should translate MyBatis PersistenceExceptions");
130     } finally {
131       connection.close(); // the template do not open the connection so it do not close it
132     }
133   }
134 
135   @Test
136   void testExceptionTranslationShouldThrowDataAccessException() {
137 
138     // this query must be the same as the query in TestMapper.xml
139     connection.getPreparedStatementResultSetHandler().prepareThrowsSQLException("SELECT 'fail'");
140 
141     try {
142       sqlSessionTemplate.selectOne("org.mybatis.spring.TestMapper.findFail");
143       fail("exception not thrown when expected");
144     } catch (MyBatisSystemException mbse) {
145       fail("SqlSessionTemplate should translate SQLExceptions into DataAccessExceptions");
146     } catch (DataAccessException dae) {
147       // success
148     } catch (Throwable t) {
149       fail("SqlSessionTemplate should translate MyBatis PersistenceExceptions");
150     }
151   }
152 
153   @Test
154   void testTemplateWithNoTxInsert() {
155 
156     sqlSessionTemplate.getMapper(TestMapper.class).insertTest("test1");
157     assertCommitJdbc();
158     assertCommitSession();
159 
160   }
161 
162   @Test
163   void testTemplateWithNoTxSelect() {
164 
165     sqlSessionTemplate.getMapper(TestMapper.class).findTest();
166     assertCommit();
167 
168   }
169 
170   @Test
171   void testWithTxRequired() {
172     var txDef = new DefaultTransactionDefinition();
173     txDef.setPropagationBehaviorName("PROPAGATION_REQUIRED");
174 
175     var status = txManager.getTransaction(txDef);
176 
177     sqlSessionTemplate.getMapper(TestMapper.class).findTest();
178 
179     txManager.commit(status);
180 
181     assertCommit();
182     assertSingleConnection();
183   }
184 
185 }