`
qinya06
  • 浏览: 595027 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

ibatis 一级缓存 功能

阅读更多
前几天和朋友讨论数据库Cache的时候,谈到了iBatis框架不支持一级缓存,后来尝试作了一些扩展来支持一级缓存,放出来大家探讨一下。

首先让我们简单回顾一下缓存的概念。直白的说,缓存就是把从数据库里查出来的数据放到内存中,供后续操作使用。例如,某个应用需要让业务人员查询前日的数据报表,可能同时有很多人在查询该数据,并且数据没有实时的要求,则可以在第一次查询的时候把结果放到缓存中,以提高后续操作的效率。缓存分为一级缓存和二级缓存,所谓一级缓存通常是指在一次数据库事物范围内的缓存,当数据库事物提交,连接被释放后,该缓存就被销毁了;所谓二级缓存通常指存在时间比较长,例如1天等。一级缓存通常在事物开启的时候建立,在事物提交或回滚的时候销毁。Hibernate对一级缓存和二级缓存都支持的很好,而ibatis则只对应用二级缓存提供了内置的支持。因此本文重点讨论如何为ibatis提供一级缓存功能。

先介绍一下我的环境:Spring1.2.6 + iBatis2.1.3 + MySql5.0。Spring已经成为事实上的标准,对iBatis提供了非常好的支持。因此我们在此讨论如何在Spring环境下提供对iBatis的一级缓存支持。Spring对iBatis的封装主要是通过SqlMapClientTemplate来实现的,而事物方面这是通过TransactionTemplate来提供支持,因此要提供一级缓存的功能,就要在这两个类上做一些文章。 当前事物的缓存可以放在当前的线程(ThreadLocal)中,看下面的代码:

public class CacheModel {

    private static final Log   logger     =
                                  LogFactory.getLog(CacheModel.class);

    private static final ThreadLocal<Map<Object, Object>> cacheModel = new ThreadLocal<Map<Object, Object>>();

    synchronized static boolean exist() {
        returncacheModel.get() != null;
    }

    public synchronized static void add(Object key, Object value) {
        if (exist()) {
            cacheModel.get().put(key, value);
        }
    }

    public synchronized static Object get(Object key) {
        if (exist()) {
            returncacheModel.get().get(key);
        }
        returnnull;
    }

    public synchronized static void initial() {
        if (!exist()) {
            cacheModel.set(new HashMap<Object, Object>());
            logger.info("Initial current transaction's Cache.");
        } else {
            thrownew RuntimeException("Current transaction's CacheModel allready initial!");
        }
    }

    public synchronized static void destroy() {
        cacheModel.set(null);
        logger.info("Destroy current transaction's Cache.");
    }

}



接下来扩展TransactionTemplate,实现在开启和结束事物的时候创建和销毁CacheModel:
publicclass CacheTransactionTemplate extends TransactionTemplate {
    privatestaticfinallongserialVersionUID = 489621858441822688L;

    privateboolean           cacheable        = true;

    privateboolean           isInitialized    = false;

    /* (non-Javadoc)
     * @see org.springframework.transaction.support.TransactionTemplate#afterPropertiesSet()
     */
    @Override
    publicvoid afterPropertiesSet() {
        super.afterPropertiesSet();

        isInitialized = true;
    }

    /* (non-Javadoc)
     * @see org.springframework.transaction.support.TransactionTemplate#execute(org.springframework.transaction.support.TransactionCallback)
     */
    @Override
    public Object execute(TransactionCallback action) throws TransactionException {
        TransactionStatus status = getTransactionManager().getTransaction(this);

        initialCacheModel(status);

        Object result = null;
        try {
            result = action.doInTransaction(status);
            logger.debug(action);
        } catch (RuntimeException ex) {
            // transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        } catch (Error err) {
            // transactional code threw error -> rollback
            rollbackOnException(status, err);
            throw err;
        }

        try {
            getTransactionManager().commit(status);
        } finally {
            destoryCacheModel(status);
        }
        return result;
    }

    /**
     *Performarollback,handlingrollbackexceptionsproperly.
     *
     *@paramstatusobjectrepresentingthetransaction
     *
     *@paramexthethrownapplicationexceptionorerror
     *
     *@throwsTransactionExceptionincaseofarollbackerror
     */
    privatevoid rollbackOnException(TransactionStatus status, Throwable ex)
                                                                            throws TransactionException {
        logger.debug("Initiating transaction rollback on application exception", ex);
        try {
            getTransactionManager().rollback(status);
        } catch (RuntimeException ex2) {
            logger.error("Application exception overridden by rollback exception", ex);
            throw ex2;
        } catch (Error err) {
            logger.error("Application exception overridden by rollback error", ex);
            throw err;
        } finally {
            destoryCacheModel(status);
        }
    }

    /**
     *Initailcurrenttransaction'sCacheModel.
     *
     *<p>
     *MustbeanewTransaction.
     *
     *Currenttransaction'sCacheModelmusthavenotbeeninitialized.
     *</p>
     *
     *@paramstatus
     */
    privatevoid initialCacheModel(TransactionStatus status) {
        if (cacheable && status != null && status.isNewTransaction()) {
            if (!CacheModel.exist()) {
                CacheModel.initial();
            }
        }
    }

    /**
     *Destroycurrenttransaction'sCacheModel.
     *
     *<p>
     *MustbeanewTransaction.
     *
     *Currenttransaction'sCacheModelmusthavebeeninitialized.
     *</p>
     *
     *@paramstatus
     */
    privatevoid destoryCacheModel(TransactionStatus status) {
        if (cacheable && status != null && status.isNewTransaction()) {
            if (CacheModel.exist()) {
                CacheModel.destroy();
            }
        }
    }

    /**
     *
     *@paramcacheable
     */
    publicvoid setCacheable(boolean cacheable) {
        if (!isInitialized) {
            this.cacheable = cacheable;
        }
    }

}



上面的代码重写了TransactionTemplate的 execute方法,加入了对CacheModel的支持。接下来再扩展SqlMapClientTemplate,并重写query方法:


publicclass CacheSqlMapClientTemplate extends SqlMapClientTemplate {

    privatestaticfinal Log    logger           = LogFactory
                                                     .getLog(CacheSqlMapClientTemplate.class);

    privatestaticfinal String DEFAULT_SPARATOR = "_";

    /* (non-Javadoc)
     * @see org.springframework.orm.ibatis.SqlMapClientTemplate#queryForList(java.lang.String, java.lang.Object, int, int)
     */
    @Override
    public List queryForList(String statementName, Object parameterObject, int skipResults,
                             int maxResults) throws DataAccessException {

        String key = statementName + DEFAULT_SPARATOR + generateKeyPart(parameterObject)
                     + skipResults + maxResults;

        List result = queryDataFromCacheModel(key, List.class);

        if (result != null) {

            return result;
        }

        result = super.queryForList(statementName, parameterObject, skipResults, maxResults);

        addDataToCacheModel(key, result);

        return result;
    }

    /* (non-Javadoc)
     * @see org.springframework.orm.ibatis.SqlMapClientTemplate#queryForList(java.lang.String, java.lang.Object)
     */
    @Override
    public List queryForList(String statementName, Object parameterObject)
                                                                          throws DataAccessException {

        String key = statementName + DEFAULT_SPARATOR + generateKeyPart(parameterObject);

        List result = queryDataFromCacheModel(key, List.class);

        if (result != null) {

            return result;
        }

        result = super.queryForList(statementName, parameterObject);

        addDataToCacheModel(key, result);

        return result;
    }

    /* (non-Javadoc)
     * @see org.springframework.orm.ibatis.SqlMapClientTemplate#queryForObject(java.lang.String, java.lang.Object, java.lang.Object)
     */
    @Override
    public Object queryForObject(String statementName, Object parameterObject, Object resultObject)
                                                                                                   throws DataAccessException {

        String key = statementName + DEFAULT_SPARATOR + generateKeyPart(parameterObject)
                     + DEFAULT_SPARATOR + generateKeyPart(resultObject);

        Object result = queryDataFromCacheModel(key, Object.class);

        if (result != null) {

            return result;
        }

        result = super.queryForObject(statementName, parameterObject, resultObject);

        addDataToCacheModel(key, result);

        return result;
    }

    /* (non-Javadoc)
     * @see org.springframework.orm.ibatis.SqlMapClientTemplate#queryForObject(java.lang.String, java.lang.Object)
     */
    @Override
    public Object queryForObject(String statementName, Object parameterObject)
                                                                              throws DataAccessException {

        String key = statementName + DEFAULT_SPARATOR + generateKeyPart(parameterObject);
        Object result = queryDataFromCacheModel(key, Object.class);
        if (result != null) {
            return result;
        }

        result = super.queryForObject(statementName, parameterObject);

        addDataToCacheModel(key, result);

        return result;
    }

    private <T> T queryDataFromCacheModel(Object key, Class<T> type) {
        if (CacheModel.exist()) {

            logger.info("Look up data from current transaction's CacheModel , KEY={" + key + "}");

            Object value = CacheModel.get(key);

            if (value != null) {
                return type.cast(value);
            } else {
                logger
                    .info("Current transaction's CacheModel dosen't contain any data relate with {"
                          + key + "}");
            }

        }
        returnnull;
    }

    private <T> void addDataToCacheModel(Object key, T value) {
        if (null == value) {
            return;
        }
        if (CacheModel.exist()) {
            CacheModel.add(key, value);
            logger.info("Add data to current transaction's CacheModel , KEY={" + key + "}");
        }
    }

    private String generateKeyPart(Object source) {
        if (source == null) {
            return"";
        } elseif (source instanceof String) {
            return (String) source;
        } elseif (source instanceof Map) {
            return generate((Map) source);
        } else {
           try {
                return generate(BeanUtils.describe(source));
            } catch (Exception e) {
                return source.toString();
            }
        }
    }

    private String generate(Map props) {
        try {
            Iterator iProps = props.keySet().iterator();
            StringBuffer retBuffer = new StringBuffer();
            while (iProps.hasNext()) {
                String key = (String) iProps.next();
                if ("class".equals(key)) {
                    continue;
                }
                retBuffer.append(key).append("=[").append(props.get(key)).append("]");
                if (iProps.hasNext()) {
                    retBuffer.append(", ");
                }
            }
            return retBuffer.toString();
        } catch (Exception e) {
            return"";
        }
    }
}



类扩展写好了,下面我们看一下如何配置:

<beans default-autowire="byName">
    <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/batchtest"/>
       <property name="username" value="root"/>
       <property name="password" value="foxdesign"/>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>

    <bean id="transactionTemplate"
       class="com.company.CacheTransactionTemplate">
       <property name="cacheable">
           <value>true</value>
       </property>   
    </bean>    

    <bean id="customSqlMapClient" 
       class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
       <property name="configLocation" value="SqlMapConfig.xml"/>
    </bean>
    
    <bean id="sqlMapClientTemplate" 
       class="com.company.CacheSqlMapClientTemplate">
       <property name="sqlMapClient" ref="customSqlMapClient"/>
    </bean>    
    
    <bean id="userCacheDAO" class="com.company.imp.UserCacheDAOImpl">
    </bean>
</beans>


经过上面的扩展,执行queryForObject和queryForList方法的时候,查询出来的数据将在当前事物内缓存起来,可以看一下下面的测试代码:
  
 publicvoid testWithTrans() {

        transactionTemplate.execute(new TransactionCallback() {

            public Object doInTransaction(TransactionStatus arg0) {

                userCacheDAO.getUserByName("Tony");

                userCacheDAO.getUserByName("Tony");

                userCacheDAO.getUserByName("Aroma");

                returnnull;
            }
        });
    }



分享到:
评论

相关推荐

    ibatis-缓存使用示例

    首先,iBATIS的缓存分为两种类型:一级缓存和二级缓存。一级缓存是SqlSession级别的,它是默认开启的,每次SqlSession内的查询结果都会被存储在一级缓存中,如果同一SqlSession内再次执行相同的SQL,会直接从缓存中...

    iBATIS缓存的使用方法

    iBATIS支持两种级别的缓存:一级缓存(默认缓存)和二级缓存。本文主要关注的是二级缓存,因为它提供了更高级别的控制和定制选项。 #### 二、缓存模型的配置 在iBATIS中,可以通过`&lt;cacheModel&gt;`元素来定义一个...

    ibatis_数据缓存

    在深入理解iBatis的Cache概念之前,首先需要知道iBatis是一个轻量级的持久层框架,它将SQL映射与Java代码分离,提供了更灵活的数据库操作方式。 **Cache概述** iBatis 提供了本地数据缓存功能,用于存储查询结果集...

    spring+ibatis+oracle分页缓存源码

    一级缓存是iBatis默认提供的,它存在于SqlSession级别,同一SqlSession内的多次查询会共享结果,避免了重复的数据库访问。然而,跨SqlSession的查询无法利用一级缓存,这时二级缓存就显得尤为重要。二级缓存是基于...

    ibatis源码,ibatis源码 ibatis源码 ibatis源码

    iBatis提供了本地缓存和二级缓存,通过CachingExecutor实现。缓存策略可以在全局配置文件中配置,也可以在每个Mapper中单独设置。源码中`org.apache.ibatis.cache.Cache`接口定义了缓存的基本操作,而具体的缓存实现...

    ibatis demo,ibatis例子,ibatis示例

    7. **缓存机制**:Ibatis内置了本地缓存和二级缓存,可以提高数据读取速度。本地缓存作用于单个SqlSession,而二级缓存则可以在多个SqlSession之间共享,但需要注意并发控制和数据一致性问题。 8. **插件支持**:...

    ibatis开发指南 经典教材

    而`OSCache`则是一个高性能的第三方缓存组件,提供了更强大的缓存功能。 总之,《ibatis开发指南》是一本详尽介绍了ibatis框架的教材,不仅涵盖了ibatis的基础概念和核心组件,还深入探讨了其高级特性和应用场景。...

    ibatis的增删改查功能

    Ibatis提供了本地缓存和二级缓存两种机制,可以提高数据读取的效率。本地缓存默认开启,作用于单个SqlSession,二级缓存则可配置为全局,共享给多个SqlSession。 六、事务管理 Ibatis支持手动和自动事务管理。默认...

    ibatis 中文手册

    4. **缓存机制**:Ibatis 内置了缓存功能,分为一级缓存(SqlSession 级别)和二级缓存(Mapper 级别),可以有效减少数据库的访问次数,提升系统性能。 **三、Java API 与 XML 配置** 1. **...

    ibatis api 帮助文档+IBATIS 开发文档

    5. **缓存**:IBATIS支持本地缓存和全局缓存,可以有效减少数据库访问,提高性能。 6. **事务管理**:讲解了如何使用IBATIS的事务管理功能,包括手动和自动提交、回滚以及事务隔离级别设置。 7. **插件**:IBATIS...

    ibatis高级特性

    Ibatis 是一款优秀的持久层框架,它简化了 Java 开发者与数据库之间的交互过程。本文将详细介绍 ibatis 的一些高级特性,包括数据关联、延迟加载、动态映射以及事务管理等方面的知识点。 #### 二、数据关联 在实际...

    ibatis api,ibatis文档,ibatis说明文档

    6. 缓存:Ibatis提供了本地缓存和二级缓存机制,有助于提高性能。 7. 执行性能:提供优化建议,如批处理、缓存使用等,以提升应用程序的运行效率。 总之,Ibatis是一个强大且灵活的Java持久层框架,其API、文档和...

    ibatis课件

    8. **缓存机制**:了解iBatis的本地缓存和二级缓存,以及如何在实际项目中合理利用缓存提高性能。 9. **最佳实践**:学习如何编写高效的iBatis代码,避免潜在的问题,提升项目的可维护性和扩展性。 通过系统学习并...

    ibatis

    5. 缓存机制:Ibatis 提供了本地缓存和二级缓存功能,可以提高数据读取的效率,减少数据库的负载。开发者可以自定义缓存策略,以满足不同场景的需求。 6. 事务管理:Ibatis 提供了基于Spring的事务管理,可以在不...

    iBatis开发指南和一个iBatis实例

    iBatis是一个轻量级的Java持久层框架,它提供了SQL映射框架,将SQL语句与Java代码分离,使得数据库访问更为灵活和可控。在本压缩包中,你将找到一系列关于iBatis的学习资源,包括PDF文档、实例代码等,这些都是深入...

    iBatis java版中文的

    默认情况下,一级缓存是SqlSession级别的,二级缓存是全局的,可以通过配置启用和定制。 6. **插件支持**:iBatis允许开发者自定义插件,拦截SQL执行过程,实现如性能监控、日志记录等功能。 在Java项目中使用...

    ibatis jar包下载

    8. **缓存**:Ibatis提供了本地缓存和二级缓存功能,这个jar包可能包含了缓存相关的类和接口。 9. **事务管理**:Ibatis提供了基于JDBC的事务控制,这个包可能包含了事务相关的类。 10. **插件支持**:Ibatis允许...

    ibatis2.3-src

    iBatis提供了本地缓存和二级缓存,能够有效减少对数据库的访问,提高性能。本地缓存作用于单个SqlSession,而二级缓存则可以跨SqlSession共享数据。 7. **事务管理** iBatis支持JDBC和Spring的事务管理,可以根据...

    ibatis 开发指南 2004

    10. **缓存机制**:解析iBatis的缓存功能,包括本地缓存和二级缓存,如何配置和使用,以及缓存的生命周期和更新策略。 11. **动态SQL**:详细阐述如何利用iBatis的动态SQL特性,通过`&lt;if&gt;`, `&lt;choose&gt;`, `&lt;when&gt;`, ...

    Ibatis教程

    6. **缓存机制**:Ibatis提供了一级缓存和二级缓存,可以提高数据读取的效率。一级缓存是SqlSession级别的,而二级缓存则是全局的,可以在多个SqlSession之间共享。 7. **事务管理**:Ibatis通过SqlSession对象来...

Global site tag (gtag.js) - Google Analytics