Spring整合MyBatis

MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由Apache Software Foundation迁移到了Google Code,并且改名为MyBatis(MyBatis源码下载地址为:http://code.google.com/p/mybatis/)

MyBatis是支持普通的SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的java对象)映射成数据库中的记录。

Spring整合MyBatis到底做了什么来简化程序猿的业务开发呢?我们一起分析一下

(1)Spring配置文件

配置文件是Spring的核心,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"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"  
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<util:properties id="db" location="classpath:db.properties">
	</util:properties>
	<bean id="dbcp" class="org.apache.commons.dbcp.BasicDataSource">
	  <property name="username" value="#{db.user}"></property>
	  <property name="password" value="#{db.pwd}"></property>
	  <property name="driverClassName" value="#{db.driver}"></property>
	  <property name="url" value="#{db.url}"></property>
	</bean>
	
	<bean id="sqlsessionfactory"
	class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dbcp"></property>
	<property name="mapperLocations"
	value="classpath:org/tarena/note/sql/*.xml">
	</property>
	</bean>
	<bean id="liuzhe" class="org.tarena.note.entity.User"></bean>
	
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<property name="basePackage" value="org.tarena.note.dao">
	</property>
	<!-- 默认引入sqlSessionFcatory -->
	</bean>
	<!-- 扫描service controller -->
	<context:component-scan base-package="org.tarena.note"/>
	<!-- handlermapping @RequestMapping,@ResponseBody,@ExceptionHandler-->
	<mvc:annotation-driven/>

(2)映射文件:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"      
 "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="org.tarena.note.dao.UserMapperDao">

<select id="findByName" parameterType="String"
resultType="org.tarena.note.entity.User">
select*from cn_user where cn_user_name=#{name}
</select>
<update id="updateToken" parameterType="map">
update cn_user set cn_user_token=#{userToken} where cn_user_id=#{userId}
</update>
</mapper>

Spring中使用MyBatis非常方便,我们甚至不知道自己在使用MyBatis.

源码分析

通过Spring整合MyBatis的示例,我们感受到了Spring为用户更加快捷地进行开发所做的努力,开发人员效率由此而得到提升。当时,相对于使用来说,我们更想知道其背后隐藏的秘密,Spring整合MyBatis如何实现的呢?我们在Spring的配置文件中,可以看到起关键作用的一个类:org.mybatis.spring.SqlSessionFactoryBean它将其他相关的bean组装到了一起,我们分析就从此类开始:

1)sqlSessionFactory创建

通过配置文件我们分析,对于配置文件的解析,Spring通过org.mybatis.Spring.SqlSessionFactoryBean封装了MyBatis实现。这个类中有两个接口:

  • InitializingBean:实现此接口的bean会在初始化时调用其afterPropertiesSet方法来进行bean的逻辑初始化。
  • FactoryBean:一旦某个bean实现此接口,那么通过getBean方法获取bean时其实是获取此类的getObject返回的实例。


我们先看看InitializingBean的aferPropertiesSet()方法

SqlSessionFactoryBean的初始化

public void afterPropertiesSet() throws Exception{
		notNull(dataSource,"Property 'dataSource' is required");
		notNull(sqlSessionFactoryBuilder,"Property 'sqlSessionFactoryBuilder' " +
				"is required");
		this.sqlSessionFactory = buildSqlSessionFactory();
	}
	
	/**
	 * 此函数的目的就是对sqlSessionFactory的初始化,通过之前展示的独立使用MyBatis的示例我们了解到SqlSessionFactory
	 * 是所有MyBatis功能的基础
	 */
	protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
		Configuration configuration;
		XMLConfigBuilder xmlConfigBuilder = null;
		if (this.configLocation != null){
			xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(),null,this.configurationProperties);
			configuration = xmlConfigBuilder.getConfiguration();
		} else {
			configuration = new Configuration();
			configuration.setVariables(this.configurationPreperties);
 		}
		if (this.objectFactory != null){
			configuration.setObjectFactory(this.objectWrapperFactory);
		}
		if (this.objectWrapperFactory != null){
			configuration.setObjectWrapperFactory(this.objectWrapperFactory);
		}
		if(haslength(this.typeAliasesPackage)){
			String[] typeAliasPackageArray = 
				tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			for(String packageToScan:typeAliasPackageArray){
				configuration.getTypeAliasRegistry().registerAliases(packageToScan,typeAliasesSuperType==null?Object.class:typeAliasesSuperType);
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Scanned package:'"+packageToScan+"' for aliases");
				}
			}
		}
		if(!isEmpty(this.typeAliases)){
			for(Class<?> typeAlias:this.typeAliases){
				configuration.getTypeAliasRegistry().registerAlias(typeAlias);
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Scanned pacakge:'" + packageToScan+"' for aliases");
				}
			}
		}
		if(!isEmpty(this.plugins)){
			for(Interceptor plugin:this.plugins){
				configuration.addInterceptor(plugin);
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Registered plugin:'"+plugin+"'");
				}
			}
		}
		if(hasLength(this.typeHandlersPackage)){
			String[] typeHandlerPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			for(String packageToScan:typeHandlersPackageArray){
				configuration.getTypeHandlerRegistry().register(packageToScan);
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Scanned package:'"+packageToScan+"'for type handlers");
				}
			}
		}
		if(!isEmpty(this.typeHandlers)){
			for(TypeHandler<?> typeHandler:this.typeHandlers){
				configuration.getTypeHandlerRegistry().register(typeHandler);
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Registered type handler:'"+typeHandler+"'");
				}
			}
		}
		if(xmlConfigBuilder != null){
			try{
				xmlConfigBuilder.parse();
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Parseed configuration file:'"+this.configLocation+"'");
				}
			}catch(Exception ex){
				throw new NestedIOException("Failed to parse config resource:"+this.configLocation,ex);
			}finally{
				ErrowContext.instance().reset();
			}
		}
		if(this.transactionFactory == null){
			this.transactionFactory = new SpringManagedTransactionFactory();
		}
	
		Environment environment = new Environment(this.environment,this.transactionFactory,this.dataSource);
		configuration.setEnvironment(environment);
		
		if(this.databaseIdProvider != null){
			try{
				configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
			}catch(SQLException e){
				throw new NestedIOException("Failed getting a databaseId",e);
			}
		}
		if(!isEmpty(this.mapperLocation)){
			for(Resource mapperLocation: this.mapperLocation){
				if(mapperLocation == null){
					continue;
				}
				try{
					XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
							configguration,mapperLocation.toString(),configuration.getSqlFragments());
					xmlMapperBuilder.parse();
				}catch(Excpetion e){
					throw new NestedIOException("Failed to parse mapping resource:'"+mapperLocation+"'",e);
				}finally{
					ErrorContext.instance.reset();
				}
				if(this.logger.isDebugEnabled()){
					this.logger.debug("Parse mapper file:'"+mapperLocation+"'");
				}
			}
		}else{
			if(this.logger.isDebugEnabled()){
				this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
			}
		}
		return this.sqlSessionFactoryBuilder.build(configuration);
	}

从以上函数可以看出,配置文件还可以支持多种属性的配置,如configLocation,objectFactory,ojectWrapperFactory,typeAliasesPackage,typeAliases,typeHandlersPackage,plugins,typeHandlers,transactionFactory,databaseIdProvider,mapperLocations.

获取sqlSessionFactoryBean实例

由于SqlSessionFactoryBean实现了FactoryBean接口,所以当通过getBean方法获取对应实例时,其实是过去该类的getObject()函数返回的实例,也就是获取初始化后的sqlSessionFactory属性。

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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值