SqlMapClientTemplate

1.引入与作用

         (1)引入

                    Spring对第三方开元orm的支持一贯做法是使用模板模式封装一个Template类,如iBatis的SqlMapClient封装为SQLMapClientTemplate,hibernate的HibernateTemplate。

   

         (2)作用

                    我们在创建Dao的时候会继承SqlMapClientDaoSupport,通过SqlMapClientDaoSupport的SqlMapClient属性来操作数据库。

                    SqlMapClientTemplate是帮我们做一些通用操作的,如:dataSource的初始化、释放数据库连接,开启/关闭Session等

                    其中最重要的SqlMapClientTemplate.execute方法,其他操作,如:queryForObject、update等都是通过传递SqlMapClientCallback给execute,execute做好上述初始化操作后,再调用SqlMapClientCallback的doInSqlMapClient方法操作数据库返回结果,然后再关闭数据库连接和Session

     

2.SqlMapClientFactoryBean

         SqlMapClientFactoryBean实际上是用来管理iBatis的Ioc容器

public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean 
{
	//当前线程绑定iBatis中blob/clob等大字段数据处理器资源
	private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>();
	public static LobHandler getConfigTimeLobHandler() 
	{
		return configTimeLobHandlerHolder.get();
	}

	//iBatis配置资源路径
	private Resource[] configLocations;
    //iBatis映射文件路径
	private Resource[] mappingLocations;
	//iBatis的SqlMapClient属性
	private Properties sqlMapClientProperties;
	//数据源
	private DataSource dataSource;
	//是否使用spring的事务包装数据源
	private boolean useTransactionAwareDataSource = true;
	//事务配置类
	private Class transactionConfigClass = ExternalTransactionConfig.class;
	//事务配置属性
	private Properties transactionConfigProperties;
	//blob/clob等lob类型处理器
	private LobHandler lobHandler;
	//iBatis的SqlMapClient
	private SqlMapClient sqlMapClient;

	//初始化事务配置文件,设置不允许事务自动提交
	public SqlMapClientFactoryBean() 
	{
		this.transactionConfigProperties = new Properties();
		this.transactionConfigProperties.setProperty("SetAutoCommitAllowed", "false");
	}

	//指定sqlMapClient资源文件路径
	public void setConfigLocation(Resource configLocation) 
	{
		this.configLocations = (configLocation != null ? new Resource[] {configLocation} : null);
	}

	//指定多个sqlMapClient资源文件路径,在运行时会合并
	public void setConfigLocations(Resource[] configLocations) 
	{
		this.configLocations = configLocations;
	}

	//设置ibatis映射文件路径
	public void setMappingLocations(Resource[] mappingLocations)
	{
		this.mappingLocations = mappingLocations;
	}

	//通过属性文件配置SqlMapClient中的属性
	public void setSqlMapClientProperties(Properties sqlMapClientProperties)
	{
		this.sqlMapClientProperties = sqlMapClientProperties;
	}

	//设置ibatis使用的数据源
	public void setDataSource(DataSource dataSource) 
	{
		this.dataSource = dataSource;
	}

	//设置是否使用事务包装
	public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) 
	{
		this.useTransactionAwareDataSource = useTransactionAwareDataSource;
	}

	//设置iBatis使用的事务配置类
	public void setTransactionConfigClass(Class transactionConfigClass) 
	{
		if (transactionConfigClass == null || !TransactionConfig.class.isAssignableFrom(transactionConfigClass)) 
		{
			throw new IllegalArgumentException("Invalid transactionConfigClass: does not implement " +
					"com.ibatis.sqlmap.engine.transaction.TransactionConfig");
		}
		this.transactionConfigClass = transactionConfigClass;
	}

	//设置事务配置属性
	public void setTransactionConfigProperties(Properties transactionConfigProperties) 
	{
		this.transactionConfigProperties = transactionConfigProperties;
	}

	//设置大字段处理器
	public void setLobHandler(LobHandler lobHandler) 
	{
		this.lobHandler = lobHandler;
	}

	//ioc容器初始化完成之后的回调方法,
	public void afterPropertiesSet() throws Exception 
	{
		//配置lob处理器
		if (this.lobHandler != null) 
		{
			configTimeLobHandlerHolder.set(this.lobHandler);
		}

		//创建iBatis的SqlMapClient
		try 
		{
			this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);

			//为创建的SqlMapClient设置数据源
			if (this.dataSource != null) 
			{
				//创建事务配置实例
				TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();
				//获取数据源
				DataSource dataSourceToUse = this.dataSource;
				//如果iBatis使用事务包装的数据源,并且当前获取到的数据源不是代理类型
				if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) 
				{
					//为指定数据源创建爱你事务包装代理
					dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);
				}
				//为事务配置对象设置数据源
				transactionConfig.setDataSource(dataSourceToUse);
				//初始化实物配置对象
				transactionConfig.initialize(this.transactionConfigProperties);
				applyTransactionConfig(this.sqlMapClient, transactionConfig);
			}
		}

		//创建SqlMapClient成功后,清楚当前县城绑定的lob处理器
		finally 
		{
			if (this.lobHandler != null) 
			{
				// Reset LobHandler holder.
				configTimeLobHandlerHolder.remove();
			}
		}
	}

	//具体创建SqlMapClient的方法
	//根据给定的iBatis配置文件,映射文件和配置中的属性文件创建SqlMapClient
	protected SqlMapClient buildSqlMapClient(
			Resource[] configLocations, Resource[] mappingLocations, Properties properties)
			throws IOException 
	{
		//如果给定iBatis配置文件路径为空
		if (ObjectUtils.isEmpty(configLocations)) 
		{
			throw new IllegalArgumentException("At least 1 'configLocation' entry is required");
		}

		SqlMapClient client = null;
		//创建ibatis配置文件解析器
		SqlMapConfigParser configParser = new SqlMapConfigParser();
		//遍历所有iBatis配置文件
		for (Resource configLocation : configLocations) 
		{
			InputStream is = configLocation.getInputStream();
			try 
			{
				//根据配置创建SqlMapClient
				client = configParser.parse(is, properties);
			}
			catch (RuntimeException ex) 
			{
				throw new NestedIOException("Failed to parse config resource: " + configLocation, ex.getCause());
			}
		}

		//如果映射文件部位空
		if (mappingLocations != null) 
		{
			//根据配置文件解析器创建映射文件解析器
			SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser);
			//遍历所有映射文件
			for (Resource mappingLocation : mappingLocations)
			{
				try 
				{
					//解析映射文件
					mapParser.parse(mappingLocation.getInputStream());
				}
				catch (NodeletException ex) 
				{
					throw new NestedIOException("Failed to parse mapping resource: " + mappingLocation, ex);
				}
			}
		}

		return client;
	}

	//将ibatis配置中指定的事务配置应用到SqlMapClient上
	protected void applyTransactionConfig(SqlMapClient sqlMapClient, TransactionConfig transactionConfig) 
	{
		//如果SqlMapClient不是ExtendedSqlMapClient类型,则无法将配置应用到SqlMapClient对象上
		if (!(sqlMapClient instanceof ExtendedSqlMapClient)) 
		{
			throw new IllegalArgumentException(
					"Cannot set TransactionConfig with DataSource for SqlMapClient if not of type " +
					"ExtendedSqlMapClient: " + sqlMapClient);
		}
		ExtendedSqlMapClient extendedClient = (ExtendedSqlMapClient) sqlMapClient;
		//设置最大并发事务数量
		transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions());
		//为SqlMapClient设置事务处理器
		extendedClient.getDelegate().setTxManager(new TransactionManager(transactionConfig));
	}

	//spring ioc容器中对应用提供的一个获取被管理对象的方法
	public SqlMapClient getObject() 
	{
		return this.sqlMapClient;
	}

	public Class<? extends SqlMapClient> getObjectType()
	{
		return (this.sqlMapClient != null ? this.sqlMapClient.getClass() : SqlMapClient.class);
	}

	//默认spring ioc中管理的对象是单例的
	public boolean isSingleton() 
	{
		return true;
	}


	//ibatis映射解析工厂类
	private static class SqlMapParserFactory 
	{
		//创建映射解析器
		public static SqlMapParser createSqlMapParser(SqlMapConfigParser configParser) 
		{
			XmlParserState state = null;
			try 
			{
				//通过反射获得SqlMapConfigParser类中的state字段的值
				Field stateField = SqlMapConfigParser.class.getDeclaredField("state");
				stateField.setAccessible(true);
				state = (XmlParserState) stateField.get(configParser);
			}
			catch (Exception ex) 
			{
				throw new IllegalStateException("iBATIS 2.3.2 'state' field not found in SqlMapConfigParser class - " +
						"please upgrade to IBATIS 2.3.2 or higher in order to use the new 'mappingLocations' feature. " + ex);
			}
			return new SqlMapParser(state);
		}
	}
}
               SqlMapClientFactoryBean实现了spring的FactoryBean接口,是spring管理iBatis的ioc容器。在ioc容器初始化过程中主要完成定位iBatis配置文件和映射文件等工作

               同时SqlMapClientFactoryBean实现了InitializingBean接口,实现了afterPropertiesSet方法,该方法时在ioc容器初始化完成之后由ioc容器进行回调的,在该方法中主要是根据定义的IBatis配置文件和映射文件创建SqlMapClient对象的过程

3.SqlMapClientTemplate

         spring通过SqlMapClientTemplate对ibatis的一些通用操作做了统一的封装处理,同时也对ibatis的api做了一些封装,方便开发者使用。

         (1)execute方法

                    和JdbcTemplate和HibernateTemplate一样,SqlMapClientTemplate也是通过execute方法封装iBatis增删改查前的通用操作,同时在execute方法中调用相应的回调对象的回调犯法来真正完成ibatis的处理操作

public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException 
{
	Assert.notNull(action, "Callback object must not be null");
	Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");

	//通过SqlMapClient对象打开一个IbatisSqlMapSession
	SqlMapSession session = this.sqlMapClient.openSession();
	if (logger.isDebugEnabled()) 
	{
		logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
	}
	Connection ibatisCon = null;

	try {
		Connection springCon = null;
		//获取数据源
		DataSource dataSource = getDataSource();
		//根据数据源是否是事务包装数据源代理类型,判断数据源是否需要事务包装
		boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);

		try 
		{
			//获取连接
			ibatisCon = session.getCurrentConnection();
			//如果当前的SqlMapSession还没有创建过连接
			if (ibatisCon == null) 
			{
				//如果ibatis数据源已经在spring的事务管理之下,则直接使用数据源创建连接
				//否则,使用DataSourceUtils创建连接,并且将其置于spring的事务管理之中
				springCon = (transactionAware ?
						dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
				session.setUserConnection(springCon);
				if (logger.isDebugEnabled()) 
				{
					logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
				}
			}
			//如果已经创建过连接,则直接使用
			else 
			{
				if (logger.isDebugEnabled()) 
				{
					logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
				}
			}
		}
		catch (SQLException ex) 
		{
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}

		//调用具体增删改查操作回调对象的方法
		try 
		{
			return action.doInSqlMapClient(session);
		}
		catch (SQLException ex)
		{
			throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
		}
		//释放连接
		finally 
		{
			try 
			{
				if (springCon != null) 
				{
					if (transactionAware) 
					{
						springCon.close();
					}
					else 
					{
						DataSourceUtils.doReleaseConnection(springCon, dataSource);
					}
				}
			}
			catch (Throwable ex) 
			{
				logger.debug("Could not close JDBC Connection", ex);
			}
		}

	}
	//关闭iBatis的SqlMapSession
	finally 
	{
		if (ibatisCon == null) 
		{
			session.close();
		}
	}
}

         (2)spring封装的iBatis api方法

                    以spring的queryForObject方法为例:

//查询对象
public Object queryForObject(final String statementName, final Object parameterObject)
		throws DataAccessException 
{
	//调用execute方法,参数是实现了SqlMapClientCallback接口的匿名内部类
	//execute方法中回调该对象的doInSqlMapClient方法
	return execute(new SqlMapClientCallback<Object>() 
			{
		        //真正调用ibatis api执行具体操作的方法
				public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException 
				{
					//调用SqlMapSession对象的queryForObject方法
					return executor.queryForObject(statementName, parameterObject);
				}
			});
}


4.使用

         (1)SqlMapClientFactoryBean的装配

                   SqlMapClientFactoryBean是SqlMapClientTemplate使用的基础,如果在spring中没有装配SqlMapClientFactoryBean,那么SqlMapClientTemp将不可用,将报空指针异常

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <!-- iBatis sqlmap config 文件位置 -->
        <property name="configLocation" value="/WEB-INF/sqlmap-config.xml"/> 
        <!-- 在SpringFramework配置文件中使用的数据源 -->
        <property name="dataSource" ref="dataSource"/> 
        <!-- 如果需要读写Lob字段,需要注入在SpringFramework配置文件中配置好的Handler,这里是Oracle的数据库 -->
        <property name="lobHandler" ref="oracleLobHandler"/>
</bean>

         (2)Dao实现类继承SqlMapClientDaoSupport类

    import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
    ......
    public class ReportDAOImpl extends SqlMapClientDaoSupport implement ReportDAO
    {
        ......
    }
         (3)在spring中为daoImpl类入住SqlMapClient实例

    <bean id="reportDao" class="com.test.dao.ReportDAOImpl">
        <!-- 装配SqlMapClientFactoryBean -->
        <property name="sqlMapClient" ref="sqlMapClient"/> 
    </bean>
         这里有一个问题:SqlMapClientFactoryBean和SqlMapClient没有任何继承或者实现关系,可以这样注入吗?这里不知道怎么解释


         以上说白了就是:Dao实现类继承SqlMapClientDaoSupport,SqlMapClientDaoSupport中有一个SqlMapClientTemplate属性,这个属性在spring配置文件中注入就可以了,注入的是SqlMapClientFactoryBean实例

         (4)使用SqlMapClientTemplate查询

                    1)没有参数的查询

List result = getSqlMapClientTemplate().queryForList("TestSpace.qryTest");
                    2)按照主键查询
Long id = new Long("2");
Object resultObj = getSqlMapClientTemplate().queryForObject("TestSpace.getTest", id);
                    3)使用pojo构成的参数查询
    ObjectA objA = new ObjectA();
    objA.setParam1("test1");
    objA.setParam2("test2");
    ......
    List result = getSqlMapClientTemplate().queryForList("TestSpace.qryTestByParam", objA);
                    4)分页查询
List result = getSqlMapClientTemplate().queryForList("TestSpace.qryTestByParam", objA, 4, 40);

         (5)使用SqlMapClientTemplate添加数据

 ObjectA objA = new ObjectA();
 objA.setParam1("test1");
 objA.setParam2("test2");
    ......
 getSqlMapClientTemplate().insert("TestSpace.insertTest", objA);
         (6)使用SqlMapClientTemplate更新数据
ObjectA objA = new ObjectA();
objA.setParam1("test1");
objA.setParam2("test2");
    ......
getSqlMapClientTemplate().update("TestSpace.updateTest", objA);
         (7)使用SqlMapClientTemplate删除数据
Long id = new Long("2");
getSqlMapClientTemplate().delete("TestSpace.deleteTest", id);


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值