SqlMapClientTemplate

来源:互联网 发布:打广告软件 编辑:程序博客网 时间:2024/04/19 18:19

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的SqlMapClientprivate 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的SqlMapClienttry {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配置文件,映射文件和配置中的属性文件创建SqlMapClientprotected 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 {//根据配置创建SqlMapClientclient = 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对象打开一个IbatisSqlMapSessionSqlMapSession 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的SqlMapSessionfinally {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);


0 0
原创粉丝点击