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

iBatis一级缓存的探讨

阅读更多
http://blog.csdn.net/ecsoftcn/archive/2007/09/09/1777904.aspx
前几天和朋友讨论数据库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() {

        return cacheModel.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()) {

            return cacheModel.get().get(key);

        }

        return null;

    }



    public synchronized static void initial() {

        if (!exist()) {

            cacheModel.set(new HashMap<Object, Object>());

            logger.info("Initial current transaction's Cache.");

        } else {

            throw new RuntimeException("Current transaction's CacheModel allready initial!");

        }

    }



    public synchronized static void destroy() {

        cacheModel.set(null);

        logger.info("Destroy current transaction's Cache.");

    }



}

接下来扩展TransactionTemplate,实现在开启和结束事物的时候创建和销毁CacheModel:

public class CacheTransactionTemplate extends TransactionTemplate {

    private static final long serialVersionUID = 489621858441822688L;



    private boolean           cacheable        = true;



    private boolean           isInitialized    = false;



    /* (non-Javadoc)

     * @see org.springframework.transaction.support.TransactionTemplate#afterPropertiesSet()

     */

    @Override

    public void 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;

    }



    /**

     * Perform a rollback, handling rollback exceptions properly.

     *

     * @param status object representing the transaction

     *

     * @param ex the thrown application exception or error

     *

     * @throws TransactionException in case of a rollback error

     */

    private void 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);

        }

    }



    /**

     * Initail current transaction's CacheModel.

     *

     * <p>

     * Must be a new Transaction.

     *

     * Current transaction's CacheModel must have not been initialized.

     * </p>

     *

     * @param status

     */

    private void initialCacheModel(TransactionStatus status) {

        if (cacheable && status != null && status.isNewTransaction()) {

            if (!CacheModel.exist()) {

                CacheModel.initial();

            }

        }

    }



    /**

     * Destroy current transaction's CacheModel.

     *

     * <p>

     * Must be a new Transaction.

     *

     * Current transaction's CacheModel must have been initialized.

     * </p>

     *

     * @param status

     */

    private void destoryCacheModel(TransactionStatus status) {

        if (cacheable && status != null && status.isNewTransaction()) {

            if (CacheModel.exist()) {

                CacheModel.destroy();

            }

        }

    }



    /**

     *

     * @param cacheable

     */

    public void setCacheable(boolean cacheable) {

        if (!isInitialized) {

            this.cacheable = cacheable;

        }

    }



}



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

public class CacheSqlMapClientTemplate extends SqlMapClientTemplate {



    private static final Log    logger           = LogFactory

                                                     .getLog(CacheSqlMapClientTemplate.class);



    private static final 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 + "}");

            }



        }

        return null;

    }



    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 "";

        } else if (source instanceof String) {

            return (String) source;

        } else if (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方法的时候,查询出来的数据将在当前事物内缓存起来,可以看一下下面的测试代码:

    public void testWithTrans() {



        transactionTemplate.execute(new TransactionCallback() {



            public Object doInTransaction(TransactionStatus arg0) {



                userCacheDAO.getUserByName("Tony");



                userCacheDAO.getUserByName("Tony");



                userCacheDAO.getUserByName("Aroma");



                return null;

            }

        });

    }



该方案只是抛砖引玉的提供了一些想法,大家有什么更好的方法可以讨论一下。
分享到:
评论

相关推荐

    Java_ibatis缓存技术

    在iBatis中,缓存被划分为一级缓存和二级缓存。 1. **一级缓存**:也称为SqlSession级别的缓存,是默认开启的。当我们在同一个SqlSession中执行相同的SQL查询时,如果第一次查询的结果已经存在于缓存中,那么第二次...

    spring+ibatis+oracle分页缓存源码

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

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

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

    ibatis开发指南 经典教材

    总之,《ibatis开发指南》是一本详尽介绍了ibatis框架的教材,不仅涵盖了ibatis的基础概念和核心组件,还深入探讨了其高级特性和应用场景。对于希望在保证性能的同时充分利用ORM框架优势的开发者来说,这本书无疑是...

    Java_ibatis-cache.rar_cache

    首先,我们要了解iBatis的缓存分为一级缓存和二级缓存。一级缓存是SqlSession级别的,也称为本地缓存,它存储在SqlSessionFactory实例中。当一个SqlSession打开时,所有在这个SqlSession中的查询结果都会被存储在一...

    ibatis课件

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

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

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

    ibatis 开发指南 2004

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

    ibatisDemo 入门源码

    2. 缓存:提供本地缓存和二级缓存,提高数据读取速度。 3. 参数映射:通过`@Param`注解或`&lt;param&gt;`标签实现参数绑定。 4. 结果映射:通过`@ResultMap`注解或`&lt;resultMap&gt;`标签进行结果集映射。 六、IbatisDemo学习...

    ibatis系列 详细介绍

    5. **缓存机制**:分析iBATIS的缓存功能,包括本地缓存和二级缓存,提高数据读取效率。 6. **动态SQL**:介绍如何利用iBATIS的动态元素(if、choose、when、otherwise等)编写灵活的SQL语句。 7. **异常处理**:...

    SpringMVC+ibatis整合

    - **缓存支持**:iBatis支持二级缓存,结合Spring的缓存抽象,可以实现更高效的缓存策略。 - **AOP编程**:Spring提供的面向切面编程能力,可以用于日志记录、权限控制等场景。 通过SpringMVC和iBatis的整合,...

    IBATIS_IN_ACTION

    《IBATIS_IN_ACTION》是一本深度探讨iBATIS框架应用与实践的专业书籍,由Clinton Begin、Brandon Goodin和Larry Meadors共同编写。本书详细介绍了iBATIS这一流行开源框架的核心概念、工作原理以及在实际项目中的应用...

    ibatis学习

    6. **缓存机制**:Ibatis提供了一级缓存和二级缓存,可以优化查询性能,减少对数据库的访问。 7. **事务管理**:Ibatis支持编程式和声明式事务管理,可以根据应用场景选择合适的方式。 8. **插件扩展**:Ibatis...

    ibatis-2.3.2.715.jar

    6. 缓存机制:为了提升性能,Ibatis提供了一级缓存和二级缓存。一级缓存是SqlSession级别的,对于同一个SqlSession内的多次查询,如果查询条件相同,会直接返回缓存的结果。二级缓存则是Mapper级别的,允许跨...

    ibatis3.0示例

    iBatis支持本地缓存和二级缓存,可以有效提高查询效率。`&lt;cache&gt;`标签在XML配置文件中定义缓存配置,通过SqlSession的getCache()方法获取缓存实例。 7. **事务管理** iBatis 3.0支持编程式和声明式事务管理。编程...

    ibatis 实例

    标题 "ibatis 实例" 暗示了我们将探讨的是一个关于 iBATIS 数据库持久化框架的实际应用案例。iBATIS 是一个流行的开源 Java 库,它为 SQL 查询提供了映射框架,允许将 SQL 语句与 Java 代码分离,从而简化数据访问层...

    ibatis jar

    4. 缓存机制:iBatis提供了本地缓存和二级缓存,以提高数据读取速度,减少数据库交互。 5. 易于集成:iBatis可以轻松地与Spring框架集成,实现事务管理和其他高级功能。 四、使用iBatis的步骤 1. 引入ibatis-2.3....

    iBatis指南

    iBatis提供了两级缓存机制,分别为SqlSession级别的缓存(一级缓存)和Mapper级别的缓存(二级缓存)。一级缓存默认开启,同一个SqlSession内的多次查询会复用之前的结果。二级缓存则可以在多个SqlSession之间共享...

    ibatis的增删改查

    Ibatis提供了一级缓存和二级缓存,可以有效提高数据读取速度。一级缓存是SqlSession级别的,而二级缓存则可以跨SqlSession共享,需要在配置文件和映射文件中开启并配置。 10. **参数处理** Ibatis提供了多种方式...

    ibatis in action 电子书(pdf)

    书中会介绍本地缓存和二级缓存的配置和使用,以及缓存策略的优化。 7. **动态SQL**:iBATIS的一大亮点在于它的动态SQL能力,可以避免大量硬编码的条件判断。书中会展示如何利用if、choose、when、otherwise等标签...

Global site tag (gtag.js) - Google Analytics