spring-mybatis與原生mybatis使用對比

加瓦攻城獅發表於2017-11-20

原生mybatis使用方法:

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
SqlSession session = sqlSessionFactory.openSession();    
try {      
           Employee employee = new Employee(null, "doubi", "1", "ddd@sys.com"); 
           EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);     
           mapper.addEmp(employee);               
           session.commit();    
} finally {
      session.close();    
}  
複製程式碼
spring使用方法,直接注入即可
@Autowired
EmployeeMapper employeeMapper複製程式碼

那麼spring為我們做了什麼?下面研究一下mybatis-spring.jar這個jar包

首先來看一下如何使用spring整合mybatis,下面是使用spring-mybatis的四種方法:

方法一:(使用MapperFactoryBean)

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>        
    <property name="configLocation" value="classpath:mybatis-config.xml"></property>        
    <!-- 自動掃描mapping.xml檔案 -->        
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>    
</bean>
<!--上面生成sqlSessionFactory的幾個方法基本相同-->

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>複製程式碼

這樣做的缺點是每一個mapper介面都要在xml裡配置一下


方法二:採用介面org.apache.ibatis.session.SqlSession的實現類org.mybatis.spring.SqlSessionTemplate

mybatis中, sessionFactory可由SqlSessionFactoryBuilder.來建立。MyBatis-Spring 中,使用了SqlSessionFactoryBean來替代。SqlSessionFactoryBean有一個必須屬性dataSource,另外其還有一個通用屬性configLocation(用來指定mybatis的xml配置檔案路徑)。

SqlSessionFactoryBean即相當於原生mybatis中的SqlSessionFactoryBuilder

<!-- spring和MyBatis完美整合,不需要mybatis的配置對映檔案 -->    
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        
    <property name="dataSource" ref="dataSource" />        
    <property name="configLocation"  value="classpath:sqlMapConfig.xml"/>        
    <!-- 自動掃描mapping.xml檔案,**表示迭代查詢,也可在sqlMapConfig.xml中單獨指定xml檔案-->        
    <property name="mapperLocations" value="classpath:com/hua/saf/**/*.xml" />    
</bean>        

<!-- mybatis spring sqlSessionTemplate,使用時直接讓spring注入即可 -->    
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">        
    <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>    
</bean>複製程式碼
//使用方法:
@Repositorypublic class UserDao{    @Resource    private SqlSessionTemplate sqlSessionTemplate;        public User getUser(int id) {        return sqlSessionTemplate.selectOne(this.getClass().getName() + ".getUser", 1);    }   }複製程式碼

為什麼可以這樣寫,來看一下SqlSessionTemplate

public class SqlSessionTemplate implements SqlSession {  private final SqlSessionFactory sqlSessionFactory;  private final ExecutorType executorType;  private final SqlSession sqlSessionProxy;  private final PersistenceExceptionTranslator exceptionTranslator;  /**   * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}   * provided as an argument.   *   * @param sqlSessionFactory   */  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());  }

........省略......
   public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,      PersistenceExceptionTranslator exceptionTranslator) {    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");    notNull(executorType, "Property 'executorType' is required");    this.sqlSessionFactory = sqlSessionFactory;    this.executorType = executorType;    this.exceptionTranslator = exceptionTranslator;    this.sqlSessionProxy = (SqlSession) newProxyInstance(        SqlSessionFactory.class.getClassLoader(),        new Class[] { SqlSession.class },        new SqlSessionInterceptor());  }

}複製程式碼

如上面程式碼所示,SqlSessionTemplate類實現了原生Mybatis中的SqlSession介面,實際上它就是原生mybatis中的SqlSession

方法三:採用抽象類org.mybatis.spring.support.SqlSessionDaoSupport提供SqlSession

    <!-- spring和MyBatis完美整合,不需要mybatis的配置對映檔案 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation"  value="classpath:sqlMapConfig.xml"/>
        <!-- 自動掃描mapping.xml檔案,**表示迭代查詢,也可在sqlMapConfig.xml中單獨指定xml檔案-->
        <property name="mapperLocations" value="classpath:com/hua/saf/**/*.xml" />
    </bean>複製程式碼
public  class BaseDao extends SqlSessionDaoSupport{  //使用sqlSessionFactory  @Autowired   private  SqlSessionFactory sqlSessionFactory;      @Autowired   public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)    {        super.setSqlSessionFactory(sqlSessionFactory);    }    /**   * 執行insert操作   * @param statement   * @return   */  public int insert(String statement) {    return getSqlSession().insert(statement);  }  /**   * 執行insert操作   * @param statement   * @param parameter   * @return   */  public int insert(String statement, Object parameter) {    return getSqlSession().insert(statement, parameter);  }    public int update(String statement) {    return getSqlSession().update(statement);  }  public int update(String statement, Object parameter) {    return getSqlSession().update(statement, parameter);  }    public int delete(String statement) {    return getSqlSession().delete(statement);  }    public int delete(String statement, Object parameter) {    return getSqlSession().delete(statement, parameter);  }    /**   * 獲取一個list集合   * @param statement   * @return   */  public List<?> selectList(String statement) {    return getSqlSession().selectList(statement);  }    /**   * 根據引數 獲取一個list集合   * @param statement   * @param parameter   * @return   */  public List<?> selectList(String statement, Object parameter) {    return getSqlSession().selectList(statement, parameter);  }    public Map<?, ?> selectMap(String statement, String mapKey) {    return getSqlSession().selectMap(statement, mapKey);  }  public Map<?, ?> selectMap(String statement, Object parameter, String mapKey) {    return getSqlSession().selectMap(statement, parameter, mapKey);  }    /**   * 獲取Object物件   * @param statement   * @return   */  public Object selectOne(String statement) {    return getSqlSession().selectOne(statement);  }    /**   * 獲取connection, 以便執行較為複雜的用法   * @return   */  public Connection getConnection() {    return getSqlSession().getConnection();  }  }複製程式碼

如上程式碼,一個Dao類繼承了SqlSessionDaoSupport類後,就可以在類中注入SessionFactory,進而通過getSqlSession()獲取當前SqlSession

下面是SqlSessionDaoSupport的原始碼,它是一個抽象類,並擁有sqlSession屬性,在setSqlSessionFactory方法中例項化了該sqlSession:

public abstract class SqlSessionDaoSupport extends DaoSupport {  private SqlSession sqlSession;  private boolean externalSqlSession;  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {    if (!this.externalSqlSession) {      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);    }  }  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {    this.sqlSession = sqlSessionTemplate;    this.externalSqlSession = true;  }  public SqlSession getSqlSession() {    return this.sqlSession;  }  protected void checkDaoConfig() {    notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");  }}複製程式碼

方法四:(也是最常見的使用方法,使用MapperScannerConfigurer,它將會查詢類路徑下的對映器並自動將它們建立成MapperFactoryBean)

由於直接使用MapperFactoryBean會在配置檔案中配置大量mapper,因此這裡使用包掃描的方式通過註解獲取該bean

<!-- spring和MyBatis完美整合,不需要mybatis的配置對映檔案 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自動掃描mapping.xml檔案,**表示迭代查詢 -->
        <property name="mapperLocations" value="classpath:com/hua/saf/**/*.xml" />
    </bean>

    <!-- DAO介面所在包名,Spring會自動查詢其下的類 ,包下的類需要使用@MapperScan註解,否則容器注入會失敗 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hua.saf.*" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>




//使用如下程式碼,即可完成注入
@Resource
private UserDao userDao;複製程式碼

下面看一下MapperScannerConfigurer這個類:

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {  private String basePackage;  private boolean addToConfig = true;  private SqlSessionFactory sqlSessionFactory;  private SqlSessionTemplate sqlSessionTemplate;  private String sqlSessionFactoryBeanName;  private String sqlSessionTemplateBeanName;  private Class<? extends Annotation> annotationClass;  private Class<?> markerInterface;  private ApplicationContext applicationContext;  private String beanName;  private boolean processPropertyPlaceHolders;  private BeanNameGenerator nameGenerator;




public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {    if (this.processPropertyPlaceHolders) {      processPropertyPlaceHolders();    }    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);    scanner.setAddToConfig(this.addToConfig);    scanner.setAnnotationClass(this.annotationClass);    scanner.setMarkerInterface(this.markerInterface);    scanner.setSqlSessionFactory(this.sqlSessionFactory);    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);    scanner.setResourceLoader(this.applicationContext);    scanner.setBeanNameGenerator(this.nameGenerator);    scanner.registerFilters();    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));  }複製程式碼
ClassPathMapperScanner :

public Set<BeanDefinitionHolder> doScan(String... basePackages) {    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);    if (beanDefinitions.isEmpty()) {      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");    } else {      for (BeanDefinitionHolder holder : beanDefinitions) {        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();        if (logger.isDebugEnabled()) {          logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()               + "' and '" + definition.getBeanClassName() + "' mapperInterface");        }        // the mapper interface is the original class of the bean        // but, the actual class of the bean is MapperFactoryBean        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());        definition.setBeanClass(MapperFactoryBean.class);        definition.getPropertyValues().add("addToConfig", this.addToConfig);        boolean explicitFactoryUsed = false;        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {          definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));          explicitFactoryUsed = true;        } else if (this.sqlSessionFactory != null) {          definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);          explicitFactoryUsed = true;        }        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {          if (explicitFactoryUsed) {            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");          }          definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));          explicitFactoryUsed = true;        } else if (this.sqlSessionTemplate != null) {          if (explicitFactoryUsed) {            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");          }          definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);          explicitFactoryUsed = true;        }        if (!explicitFactoryUsed) {          if (logger.isDebugEnabled()) {            logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");          }          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);        }      }    }    return beanDefinitions;  }複製程式碼

總結:spring-mybatis與原生Mybatis相比,如下概念:

1)SqlSessionFactory類在兩者中都存在

2)前者用SqlSessionFactoryBean生成SqlSessionFactory,後者則使用SqlSessionFactoryBuilder;

3)前者使用SqlSessionTemplate,後者使用SqlSession,實際上前者實現了後者

4)MapperFactoryBean中實現了原生mybatis中下面的步驟,因此通過該類可以直接獲取到一個mapper介面的實現物件

 EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);    複製程式碼

以上是個人總結,如有錯誤,歡迎指正


相關文章