spring/springmvc+mybatis在整合时,可以在applicationContent.xml文件中进行spring,springmvc,事务管理,数据库连接池等以及与Mybatis整合的配置,当然也可以分开配置各自的xml文件。在mybatis-config.xml中主要进行一些别名,查询的分页方式的配置。例如:
applicationContext.xml中与mybatis的整合配置:
<!--由mybatis.xml文件构建一个sqlSessionFactory的实例--> <bean id="sqlSessionFactory" class="com.minmate.web.dao.mybatis.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:/config/mybatis/mybatis.xml" /> <!--配置数据源--> <property name="dataSource" ref="dataSource" /> <!--配置实体类的Mapper.xml文件--> <property name="mapperLocations" value="classpath:/config/mybatis/mapper/**/*Mapper.xml" /> </bean> mybatis.xml中的配置: <configuration> <typeAliases> <typeAlias alias="Pair" type="com.minmate.util.Pair" /> </typeAliases> <!-- 使用数据库的物理分页方式,iBatis默认分页方式采用游标数据量时候严重影响性能 --> <plugins> <plugin interceptor="com.minmate.web.dao.mybatis.support.OffsetLimitInterceptor"> <property name="dialectClass" value="com.minmate.web.dao.mybatis.support.MySQLPageDialect" /> </plugin> </plugins> </configuration>
applicationContext.xml配置mybatis的映射文件,除了用
<property name="mapperLocations" value="classpath:/config/mybatis/mapper/**/*Mapper.xml" />
也可以使用MapperScannerConfigurer扫描basePackage指定的包,找到映射接口类和映射XML文件,并注入:
<!-- 扫描mybatis映射接口类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.mybatis.mapper"/> <property name="sqlSessionFactoryBeanName" value="lazySqlSessionFactory"/> </bean>
这个配置的前提条件是:映射接口类文件(.java)和映射XML文件(.xml)需要放在相同的包下(com.mybatis.mapper)
接下来主要理理与数据库操作有关的sqlSession是如何产生的。
在我们的项目中创建SqlSessionFactory实例时,使用的SqlSessionFactoryBean类实现了FactoryBean
package org.springframework.beans.factory; public abstract interface FactoryBean<T> { /* 返回由 FactoryBean 创建的 Bean 实例,如果 isSingleton() 返回 true , 则该实例会放到Spring 容器单实例缓存池中 **/ public abstract T getObject() throws Exception; /* 返回 FactoryBean 创建的 Bean 类型 **/ public abstract Class<?> getObjectType(); /* FactoryBean 创建的 Bean 实例的作用域是 singleton 还是 prototype **/ public abstract boolean isSingleton(); }
注:
当配置文件中
@Override public SqlSessionFactory getObject() { return sqlSessionFactory; }
如果要得到FactoryBean本身,则需要在使用getBean(beanName)方法时,beanName前加上“&”,如:getBean(“&beanName”)
InitializingBean接口:
package org.springframework.beans.factory; public abstract interface InitializingBean { /* 在所有属性被设置完之后,容器会调用afterPropertiesSet()方法, 应用对象可以在这里执行任何定制的初始化操作(init-method方法与它的区别——不依赖springAPI, 从而降低与spring的耦合) **/ public abstract void afterPropertiesSet() throws Exception; }
在我们的项目中afterPropertiesSet()方法的重写:
@Override public void afterPropertiesSet() throws IOException { if( configLocation==null ) throw new IOException("configLocation in SqlSessionFactoryBean can't be null."); this.sqlSessionFactory = createSqlSessionFactory(); }
利用createSqlSessionFactory()方法创建了一个sqlSessionFactory实例,getObject()得到的就是这个sqlSessionFactory。这个产生sqlSessionFactory的方法中关键的代码:
//封装配置文件的流 reader = new InputStreamReader(new ByteArrayInputStream(XMLHelper.toString(document).getBytes("utf-8"))); // 创建sqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //将解析配置文件后的内容放入Configuration Configuration conf = sqlSessionFactory.getConfiguration();
可以看出是通过SqlSessionFactoryBuilder这个类的build方法来创建的,查看一下SqlSessionFactoryBuilder.java的源码,其中就只有build方法的重载,部分实现如下:
package org.apache.ibatis.session; public SqlSessionFactory build(Reader reader, String environment, Properties props) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, props); Configuration config = parser.parse(); return build(config); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) {} } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
可以看到SqlSessionFactoryBuilder类只是解析了xml文件然后将解析后的内容赋给一个Configuration对象,而真正创建SqlSessionFactory的是DefaultSessionFactory类,它是实现SqlSessionFactory接口的类,SqlSessionFactory接口的源码:
package org.apache.ibatis.session; import java.sql.Connection; public abstract interface SqlSessionFactory { public abstract SqlSession openSession(); public abstract SqlSession openSession(boolean paramBoolean); public abstract SqlSession openSession(Connection paramConnection); public abstract SqlSession openSession(TransactionIsolationLevel paramTransactionIsolationLevel); public abstract SqlSession openSession(ExecutorType paramExecutorType); public abstract SqlSession openSession(ExecutorType paramExecutorType, boolean paramBoolean); public abstract SqlSession openSession(ExecutorType paramExecutorType, TransactionIsolationLevel paramTransactionIsolationLevel); public abstract SqlSession openSession(ExecutorType paramExecutorType, Connection paramConnection); public abstract Configuration getConfiguration(); }
可以看到SqlSessionFactory接口中包含了一系列可以得到SqlSession对象的抽象重载方法,由类DefaultSqlSessionFactory来实现这个接口,其中部分实现以及几个关键的方法为:
package org.apache.ibatis.session.defaults; ... private static final Log log = LogFactory.getLog(Connection.class); private final Configuration configuration; private final TransactionFactory managedTransactionFactory; /**构造函数初始化*/ public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; this.managedTransactionFactory = new ManagedTransactionFactory(); } public SqlSession openSession() { return openSessionFromDataSource(this.configuration.getDefaultExecutorType(), null, false); } ... **********************************关键的方法************************************** /** 根据数据源创建session @params execType执行器的类型 level事物隔离级别 autoCommit是否自动提交 */ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit){...} /**根据连接对象创建session*/ private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection){...} /**从环境中获取数据源*/ private DataSource getDataSourceFromEnvironment(Environment environment){...} /**从环境配置中获取事务工厂*/ private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment){...} /**包装连接,使连接拥有日志功能*/ private Connection wrapConnection(Connection connection){...}
创建的SqlSession 包含了所有执行数据库SQL 语句的方法,能够直接地通过SqlSession 实例执行映射SQL 语句。其源码接口中定义了数据库操作的一系列抽象方法:
package org.apache.ibatis.session; import java.sql.Connection; import java.util.List; public abstract interface SqlSession { public abstract Object selectOne(String paramString); public abstract Object selectOne(String paramString, Object paramObject); public abstract List selectList(String paramString); public abstract List selectList(String paramString, Object paramObject); public abstract List selectList(String paramString, Object paramObject, RowBounds paramRowBounds); public abstract void select(String paramString, Object paramObject, ResultHandler paramResultHandler); public abstract void select(String paramString, Object paramObject, RowBounds paramRowBounds, ResultHandler paramResultHandler); public abstract int insert(String paramString); public abstract int insert(String paramString, Object paramObject); public abstract int update(String paramString); public abstract int update(String paramString, Object paramObject); public abstract int delete(String paramString); public abstract int delete(String paramString, Object paramObject); public abstract void commit(); public abstract void commit(boolean paramBoolean); public abstract void rollback(); public abstract void rollback(boolean paramBoolean); public abstract void close(); public abstract void clearCache(); public abstract Configuration getConfiguration(); public abstract <T> T getMapper(Class<T> paramClass); public abstract Connection getConnection(); }
由DefaultSqlSession类来实现该接口,部分源码为:
package org.apache.ibatis.session.defaults; ... public class DefaultSqlSession implements SqlSession { private Configuration configuration; private Executor executor; private boolean autoCommit; private boolean dirty; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.autoCommit = autoCommit; this.dirty = false; } ... public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = this.configuration.getMappedStatement(statement); this.executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ... public int update(String statement, Object parameter) { try { this.dirty = true; MappedStatement ms = this.configuration.getMappedStatement(statement); return this.executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ... }
可以看到真正对数据库进行操作的是executor对象,org.apache.ibatis.executor包中的Executor接口封装了数据库的操作方法,这个接口和它的实现类的关系:

执行器(Executor)类型只有三种
SIMPLE:普通的执行器;
REUSE:执行器会重用预处理语句(prepared statements);
BATCH:执行器将重用语句并执行批量更新。
具体类型的指定是在用openSession()方法创建sqlSession传入参数指定的,执行器的实现类里面封装了最原始的JDBC操作。
了解了sqlSession的整个产生过程,看看我们的项目中是怎么来使用的。在我们的项目中用一个MyBatisDaoSupport类来执行DAO层的操作:
public class MyBatisDaoSupport extends DaoSupport { protected final Logger log = Logger.getLogger(getClass()); /** factory */ private SqlSessionFactory sqlSessionFactory; /** session template */ private SqlSessionTemplate sqlSessionTemplate; @Resource private SqlSessionFactoryBean sqlSessionFactoryBean; @PostConstruct public void init() { /**获取sqlSessionFactory*/ this.sqlSessionFactory = sqlSessionFactoryBean.getObject(); /**实例化sqlSession持久化模版*/ this.sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); } ... /** SqlSessionTemplate */ public static class SqlSessionTemplate { /** factory */ private SqlSessionFactory sqlSessionFactory; /** 构造函数 */ public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } /** execute */ public Object execute(SqlSessionCallback action) { SqlSession session = null; try { /**通过openSession()方法获取session,上面已经说过*/ session = sqlSessionFactory.openSession(); return action.doInSession(session); } finally { if( session!=null ) { session.commit(); session.close(); session = null; } } } /** 插入记录 */ public int insert(final String statement) { /**这里的SqlSessionCallback是匿名内部类,用来返回执行sql语句后的结果*/ return (Integer)execute(new SqlSessionCallback() { @Override public Object doInSession(SqlSession session) { return session.insert(statement); } }); } ... } /** session 回调 */ private static interface SqlSessionCallback { public Object doInSession(SqlSession session); } ... }
其中在静态内部类SqlSessionTemplate中有一个execute()方法,其使用一个匿名内部类作为参数,该类实际上实现了SqlSessionCallback接口 ,其余的insert、update等方法内都调用了execute()方法将其结果返回,返回的实际上是该匿名内部类实现的接口SqlSessionCallback中doInSession()方法执行后的结果,而在doInSession()方法中就使用sqlSession对象进行sql语句的映射操作。
这段匿名内部类的写法等价于如下代码:
/** session 回调 */ private static interface SqlSessionCallback { public Object doInSession(SqlSession session); } private static class SqlSessionCallbackImpl implements SqlSessionCallback { private String statement; Object param; int firstResult, maxResults; SqlSessionCallbackImpl(String statement, Object param, int firstResult, int maxResults) { this.statement = statement; this.firstResult = firstResult; this.maxResults = maxResults; this.param = param; } @Override public Object doInSession(SqlSession session) { return session.selectList(statement, param, new RowBounds(firstResult, maxResults)); }; } /** 分页参数查询 */ public List<?> selectList(final String statement, final Object param, final int firstResult, final int maxResults) { SqlSessionCallbackImpl sqlSessionCallbackImpl = new SqlSessionCallbackImpl(statement,param,firstResult,maxResults); return (List<?>)execute(sqlSessionCallbackImpl); }