原生的Mybatis是怎么使用的?

只列举关键代码

public static void main(String[] args) throws Exception {
	Reader reader=Resources.getResourceAsReader("resource/configuration.xml");
	SqlSessionFactory sessionFactory=new SqlSessionFactoryBuilder().build(reader);
	SqlSession session=sessionFactory.openSession();
	PersonDao personDao=session.getMapper(PersonDao.class);
	Person person=new Person("11","Fighter168");
	personDao.save(person);
	//这里一定要提交,不然数据无法插入
	session.commit();
	session.close();
}
Mybatis+Spring是怎么使用的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
	     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
	     <property name="url" value="jdbc:mysql://localhost:3306/test"/>
	     <property name="username" value="root"/>
	     <property name="password" value="123abc"/>
	     <!-- 连接池启动时候的初始连接数 -->
	     <property name="initialSize" value="10"/>
	     <!-- 最小空闲值 -->
	     <property name="minIdle" value="5"/>
	     <!-- 最大空闲值 -->
	     <property name="maxIdle" value="20"/>
	     <property name="maxWait" value="2000"/>
	     <!-- 连接池最大值 -->
	     <property name="maxActive" value="50"/>
	     <property name="logAbandoned" value="true"/>
	     <property name="removeAbandoned" value="true"/>
	     <property name="removeAbandonedTimeout" value="180"/>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="classpath:/resource/cfg.xml"/>
		<property name="dataSource" ref="dataSource"/>
	</bean>
		
	<bean id="personDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="net.itaem.dao.PersonDao"/>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
	</bean>
</beans>
public static void main(String[] args) {
	ApplicationContext context=new ClassPathXmlApplicationContext("resource/ApplicationContext.xml");
	PersonDao personDao=(PersonDao) context.getBean("personDao");
	Person person=new Person("12","Fighter168");
	personDao.save(person);
}

通过上面的比较,我们发现,在Spring中使用MyBatis是相当方便的,不需要我们去关注sqlSessionFactory以及SqlSession的管理,也不需要我们去操作事务相关。
至于Spring到底做了什么工作,下面让我们进入主题,一探究竟。

spring整合mybatis原理

看spring的配置文件,配置了两个比较关键的类,SqlSessionFactoryBeanMapperFactoryBean

SqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

SqlSessionFactoryBean这个类实现了三个接口,一个是InitializingBean,另一个是FactoryBean,还有就是ApplicationListener接口。下面说明一下实现了这三个接口的,有什么作用

InitializingBean接口:实现了这个接口,那么当bean初始化的时候,spring就会调用该接口的实现类的afterPropertiesSet方法,去实现当spring初始化该Bean 的时候所需要的逻辑。

FactoryBean接口:实现了该接口的类,在调用getBean的时候会返回该工厂返回的实例对象,也就是再调一次getObject方法返回工厂的实例。

ApplicationListener接口:实现了该接口,如果注册了该监听的话,那么就可以了监听到Spring的一些事件,然后做相应的处理

所以我们先来看afterPropertiesSet方法,看看做了什么事

public void afterPropertiesSet() throws Exception {
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = this.buildSqlSessionFactory();
}

从中我们可以看到,sqlSessionFactory的实例化便在这个方法里面实例化,buildSqlSessionFactory()方法会对我们的sqlSessionFactory做定制的初始化.

再来看getObject方法

public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        this.afterPropertiesSet();
    }

    return this.sqlSessionFactory;
}

可以看到,返回的其实是SqlSessionFactory

MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport继承DaoSupport,DaoSupport实现了InitializingBean接口,让我们开看看它这接口的实现:

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig();

	// Let concrete implementations initialize themselves.
	try {
		initDao();
	}
	catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}
}

该方法主要包含两个功能,一个是调用checkDaoConfig()方法,一个是调用initDao方法。checkDaoConfig方法在DaoSupport是抽象方法,让我看看它在MapperFactoryBean的实现:

 /**
  * {@inheritDoc}
  */
 @Override
 protected void checkDaoConfig() {
   super.checkDaoConfig();

   notNull(this.mapperInterface, "Property 'mapperInterface' is required");

   Configuration configuration = getSqlSession().getConfiguration();
   if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
     try {
       configuration.addMapper(this.mapperInterface);
     } catch (Throwable t) {
       logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
       throw new IllegalArgumentException(t);
     } finally {
       ErrorContext.instance().reset();
     }
   }
 }

该方法主要是检查dao的配置,主要是检验sqlSessionFactory和mapperInterface属性不能为空,以及检测接口对于的映射文件是否存在,如果存在,那么就把它添加到configuration里面去,注册mapper。
getObject()
MapperFactoryBean实现了FactoryBean接口,那么在调用getBean方法获取MapperFactoryBean实例的时候,实际上调用的就是getObject方法,让我们来看看getObject的实现:

public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

看到这里,我们会恍然大悟,原来在这里封装了getMapper操作,返回接口的实例,怪不得在Spring中使用MyBatis不用管理sqlSession了。

总结

spring整合mybatis原理其实就是通过两个类:SqlSessionFactoryBeanMapperFactoryBean,其中SqlSessionFactoryBean通过实现Spring的FactoryBeanFactoryBean两大接口,来定义如何构建和获取SqlSessionFactory,然后我们把SqlSessionFactoryBean注入到MapperFactoryBean里面的时候,其实就相当于注入了SqlSessionFactory,而MapperFactoryBean也实现了FactoryBean接口,通过getObject封装了getMapper的操作.