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