/*
* Copyright 2010-2012 The MyBatis Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.spring;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.fail;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.junit.After;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import com.mockrunner.mock.ejb.MockUserTransaction;
import com.mockrunner.mock.jdbc.MockConnection;
import com.mockrunner.mock.jdbc.MockDataSource;
import com.mockrunner.mock.jdbc.MockPreparedStatement;
/**
* @version $Id$
*/
public final class MyBatisSpringTest extends AbstractMyBatisSpringTest {
private SqlSession session;
@After
public void validateSessionClose() {
// assume if the Executor is closed, the Session is too
if ((session != null) && !executorInterceptor.isExecutorClosed()) {
session = null;
fail("SqlSession is not closed");
} else {
session = null;
}
}
// ensure MyBatis API still works with SpringManagedTransaction
@Test
public void testMyBatisAPI() {
session = sqlSessionFactory.openSession();
session.getMapper(TestMapper.class).findTest();
session.close();
assertNoCommit();
assertSingleConnection();
assertExecuteCount(1);
}
@Test
public void testMyBatisAPIWithCommit() {
session = sqlSessionFactory.openSession();
session.getMapper(TestMapper.class).findTest();
session.commit(true);
session.close();
assertCommit();
assertSingleConnection();
assertExecuteCount(1);
}
@Test
public void testMyBatisAPIWithRollback() {
session = sqlSessionFactory.openSession();
session.getMapper(TestMapper.class).findTest();
session.rollback(true);
session.close();
assertRollback();
assertSingleConnection();
assertExecuteCount(1);
}
// basic tests using SqlSessionUtils instead of using the MyBatis API directly
@Test
public void testSpringAPI() {
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.getMapper(TestMapper.class).findTest();
SqlSessionUtils.closeSqlSession(session, sqlSessionFactory);
assertNoCommit();
assertSingleConnection();
assertExecuteCount(1);
}
@Test
public void testSpringAPIWithCommit() {
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.getMapper(TestMapper.class).findTest();
session.commit(true);
SqlSessionUtils.closeSqlSession(session, sqlSessionFactory);
assertCommit();
assertSingleConnection();
}
@Test
public void testSpringAPIWithRollback() {
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.getMapper(TestMapper.class).findTest();
session.rollback(true);
SqlSessionUtils.closeSqlSession(session, sqlSessionFactory);
assertRollback();
assertSingleConnection();
}
@Test
public void testSpringAPIWithMyBatisClose() {
// This is a programming error and could lead to connection leak if there is a transaction
// in progress. But, the API allows it, so make sure it at least works without a tx.
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.getMapper(TestMapper.class).findTest();
session.close();
assertNoCommit();
assertSingleConnection();
}
// Spring API should work with a MyBatis TransactionFactories
@Test
public void testWithNonSpringTransactionFactory() {
Environment original = sqlSessionFactory.getConfiguration().getEnvironment();
Environment nonSpring = new Environment("non-spring", new JdbcTransactionFactory(), dataSource);
sqlSessionFactory.getConfiguration().setEnvironment(nonSpring);
try {
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.getMapper(TestMapper.class).findTest();
SqlSessionUtils.closeSqlSession(session, sqlSessionFactory);
// users need to manually call commit, rollback and close, just like with normal MyBatis
// API usage
assertNoCommit();
assertSingleConnection();
} finally {
sqlSessionFactory.getConfiguration().setEnvironment(original);
}
}
// Spring TX, non-Spring TransactionFactory, Spring managed DataSource
// this should not work since the DS will be out of sync with MyBatis
@Test(expected = TransientDataAccessResourceException.class)
public void testNonSpringTxFactoryWithTx() throws Exception {
Environment original = sqlSessionFactory.getConfiguration().getEnvironment();
Environment nonSpring = new Environment("non-spring", new JdbcTransactionFactory(), dataSource);
sqlSessionFactory.getConfiguration().setEnvironment(nonSpring);
TransactionStatus status = null;
try {
status = txManager.getTransaction(new DefaultTransactionDefinition());
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
fail("should not be able to get an SqlSession using non-Spring tx manager when there is an active Spring tx");
} finally {
// rollback required to close connection
txManager.rollback(status);
sqlSessionFactory.getConfiguration().setEnvironment(original);
}
}
// Spring TX, non-Spring TransactionFactory, MyBatis managed DataSource
// this should work since the DS is managed MyBatis
@Test
public void testNonSpringTxFactoryNonSpringDSWithTx() throws java.sql.SQLException {
Environment original = sqlSessionFactory.getConfiguration().getEnvironment();
MockDataSource mockDataSource = new MockDataSource();
mockDataSource.setupConnection(createMockConnection());
Environment nonSpring = new Environment("non-spring", new JdbcTransactionFactory(), mockDataSource);
sqlSessionFactory.getConfiguration().setEnvironment(nonSpring);
TransactionStatus status = null;
try {
status = txManager.getTransaction(new DefaultTransactionDefinition());
session = SqlSessionUtils.getSqlSession(sqlSessionFactory);
session.commit();
session.close();
txManager.commit(status);
// txManager still uses original connection
assertCommit();
assertSingleConnection();
// SqlSession uses its own connection
// that connection will not have commited since no SQL was executed by the session
MockConnection mockConnection = (MockConnection) mockDataSource.getConnection();
assertEquals("should call commit on Connection", 0, mockConnection.getNumberCommits());
assertEquals("should not call rollback on Connection", 0, mockConnection.getNumberRollbacks());
assertCommitSession();
} finally {
SqlSessionUtils.closeSqlSession(session, sqlSessionFactory);
sqlSessionFactory.getConfiguration().setEnvironment(original);
}
}
@Test(expected = TransientDataAccessResourceException.class)
public void testChangeExecutorTypeInTx() throw