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

执行器(Executor)类型只有三种
SIMPLE:普通的执行器;
REUSE:执行器会重用预处理语句(prepared statements);
BATCH:执行器将重用语句并执行批量更新。
具体类型的指定是在用openSession()方法创建sqlSession传入参数指定的,执行器的实现类里面封装了最原始的JDBC操作。
了解了sqlSession的整个产生过程,看看我们的项目中是怎么来使用的。在我们的项目中用一个MyBatisDaoSupport类来执行DAO层的操作:
1public class MyBatisDaoSupport extends DaoSupport 2{ 3 protected final Logger log = Logger.getLogger(getClass()); 4 5 /** factory */ 6 private SqlSessionFactory sqlSessionFactory; 7 8 /** session template */ 9 private SqlSessionTemplate sqlSessionTemplate; 10 11 @Resource 12 private SqlSessionFactoryBean sqlSessionFactoryBean; 13 14 @PostConstruct 15 public void init() 16 { 17 /**获取sqlSessionFactory*/ 18 this.sqlSessionFactory = sqlSessionFactoryBean.getObject(); 19 /**实例化sqlSession持久化模版*/ 20 this.sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); 21 } 22 ... 23 24 25 /** SqlSessionTemplate */ 26 public static class SqlSessionTemplate 27 { 28 /** factory */ 29 private SqlSessionFactory sqlSessionFactory; 30 31 /** 构造函数 */ 32 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) 33 { 34 this.sqlSessionFactory = sqlSessionFactory; 35 } 36 37 /** execute */ 38 public Object execute(SqlSessionCallback action) 39 { 40 SqlSession session = null; 41 42 try 43 { 44 /**通过openSession()方法获取session,上面已经说过*/ 45 session = sqlSessionFactory.openSession(); 46 return action.doInSession(session); 47 } 48 finally 49 { 50 if( session!=null ) 51 { 52 session.commit(); 53 session.close(); 54 session = null; 55 } 56 } 57 } 58 59 /** 插入记录 */ 60 public int insert(final String statement) 61 { 62 /**这里的SqlSessionCallback是匿名内部类,用来返回执行sql语句后的结果*/ 63 return (Integer)execute(new SqlSessionCallback() 64 { 65 @Override 66 public Object doInSession(SqlSession session) 67 { 68 return session.insert(statement); 69 } 70 }); 71 } 72 73 ... 74 } 75 76 77 /** session 回调 */ 78 private static interface SqlSessionCallback 79 { 80 public Object doInSession(SqlSession session); 81 } 82 ... 83}
其中在静态内部类SqlSessionTemplate中有一个execute()方法,其使用一个匿名内部类作为参数,该类实际上实现了SqlSessionCallback接口 ,其余的insert、update等方法内都调用了execute()方法将其结果返回,返回的实际上是该匿名内部类实现的接口SqlSessionCallback中doInSession()方法执行后的结果,而在doInSession()方法中就使用sqlSession对象进行sql语句的映射操作。
这段匿名内部类的写法等价于如下代码:
1 /** session 回调 */ 2 private static interface SqlSessionCallback 3 { 4 public Object doInSession(SqlSession session); 5 } 6 private static class SqlSessionCallbackImpl implements SqlSessionCallback 7 { 8 private String statement; Object param; int firstResult, maxResults; 9 SqlSessionCallbackImpl(String statement, Object param, int firstResult, int maxResults) 10 { 11 this.statement = statement; 12 this.firstResult = firstResult; 13 this.maxResults = maxResults; 14 this.param = param; 15 } 16 17 @Override 18 public Object doInSession(SqlSession session) 19 { 20 return session.selectList(statement, param, new RowBounds(firstResult, maxResults)); 21 }; 22 } 23 24 /** 分页参数查询 */ 25 public List<?> selectList(final String statement, final Object param, final int firstResult, final int maxResults) 26 { 27 SqlSessionCallbackImpl sqlSessionCallbackImpl = new SqlSessionCallbackImpl(statement,param,firstResult,maxResults); 28 return (List<?>)execute(sqlSessionCallbackImpl); 29 }