1) 概述:
听说公司有tc cache,可是没见着它在那里被应用了。而且他平时同事的编码中,每次要用到字典表的数据,总是QueryManager查询数据库。
昨天花了几个小时写了个简单的TCHelper,用户缓存TC表。
一般的情况下,我们字典表是不会发生变化的,有必要去cache我的字典表。
我的大体思路如下:
一:通过xml配置文件,配置所有的字典表查询sql.这样我们的sql和代码可以不在相干了。
这其中的sql有两种可能:
  1:没有参数,对于这种sql,在初始化的时候就将得到数据,并且缓存起来。
  2:带有参数的,这类sql我们没有办法再初始化的时间就执行,我们在xml中添加了一个简单的attribute 
init="false" 二 :重新加载功能,这有两种可能:
  1:字典表数据放生改变
  2:配置的xml文件放生了变化。
对于字典表数据放生改变的情况,目前这个东西只是简单实现,并没有去检测数据库的数据,而是需要用户主动的方法
TCHelper.touch();
去修改配置文件最后更新时间。
我们只检测文件是否放生变化,如果放生变化才会去重新加载数据。2) 代码

/**//*
 * Copyright (c) 2005 Print Information System Co.,Ltd. All Rights Reserved.
 */
package com.jxlt.adt.util;

import com.ptf.datastore.QueryManager;
import com.ptf.util.ClassLoaderUtil;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.File;
import java.sql.SQLException;
import java.util.*;


/** *//**
 * 字典表cache
 *
 * @author martin xus
 * @version 1.0 ,2005-10-26 11:29:33
 */

public class TCHelper 
{
    ///---------------------------------------------------------------
    /// Instancd Data
    ///---------------------------------------------------------------
    private static final Logger logger = Logger.getLogger(TCHelper.class);

    private static Map sqlMap = new HashMap();
    private static Map cache = new HashMap();
    private static long lastModefied;
    public static final String CONFIG_FILE = "tc_adt_sql.xml";

    //---------------------------------------------------------------
    //  static block
    //---------------------------------------------------------------

    static 
{
        logger.info("初始化TC Cache
..");
        init();
        logger.info("初始化TC Cache完成");
    }
    //---------------------------------------------------------------
    //  public method
    //---------------------------------------------------------------


    /** *//**
     * 根据指定的id返回缓存中的字典表数据
     * 首先检测是否需要重新加载,若需要,则先加载
     *
     * @param key 配置在xml中的id
     * @return key对应的字典表数据,
     *         若没有对应的key,则返回emptyList
     */

    public static List get(String key) 
{
        logger.info("get tc value with key:" + key);
        if (StringUtils.isBlank(key))
            return null;


        if (reload()) 
{
            logger.info("reloading
");
            init();
            logger.info("reloaded");
        }
        String _key = key.toLowerCase();

        if (cache.containsKey(_key))
            return getValue(key);
        else
            return emptyList();
    }



    /** *//**
     * 这只针对于在初始化(init)没有初始化的字典表
     *
     * @param key    xml配置文件中对应的id
     * @param params sql参数
     * @return key对应的字典表数据,
     *         若没有对应的key,则返回emptyList
     */

    public static List get(String key, List params) 
{
        logger.info("PageHelper.getTCValue: key=" + key + " params=" + params);
        if (StringUtils.isBlank(key))
            return emptyList();

        if (null == params)
            throw new UnsupportedOperationException("不支持params为空的查询!");

        String _key = key.toLowerCase();


        if (sqlMap.containsKey(_key)) 
{
            TCModel model = (TCModel) sqlMap.get(_key);
            //logger.info("model:" + model);

            try 
{
                //todo:是否cache该变量
//                cache.put(_key, _list);
                return QueryManager.excuteSql(model.getSql(), params);

            } catch (SQLException e) 
{
                return emptyList();
            }

        } else 
{
            logger.debug("invalid key!");
        }
        return emptyList();
    }


    /** *//**
     * 修改文件的最后修改时间
     * 这样当用户在查询字典表数据的时候,会重新init加载字典表数据
     * 只有在字典表数据发生修改的时候才需要调用该方法。
     */

    public static void touch() 
{
        File file = getFile();
        file.setLastModified(System.currentTimeMillis());
    }


    /** *//**
     * 清除所有的cache,包括 cache 和 sqlMap
     */

    public static void clearAll() 
{
        cache.clear();
        sqlMap.clear();
    }


    /** *//**
     * 清除指定key对应的字典表数据
     *
     * @param key 配置在xml文件中的id名称
     */

    public static void clear(String key) 
{
        if (StringUtils.isBlank(key))
            return;

        String _key = key.toLowerCase();

        if (cache.containsKey(_key))
            cache.remove(_key);
    }
    //---------------------------------------------------------------
    //  private method
    //---------------------------------------------------------------


    /** *//**
     * 读取xml文件,初始化tc cache
     */

    private static void init() 
{
        logger.info("TCHelper.init() begin");
        logger.info("Reading config from " + CONFIG_FILE);
        File file = getFile();
        lastModefied = file.lastModified();
        logger.debug("file loaded.");
        Element element = getRootElement(file);
        Iterator iterator = element.getChildren().iterator();

        while (iterator.hasNext()) 
{
            TCModel model = new TCModel();
            Element e = (Element) iterator.next();
            String id = e.getAttributeValue("id");
            if (StringUtils.isBlank(id))
                continue;
            String key = id.toLowerCase();
            //
            model.setId(key);
            model.setAmount(e.getAttributeValue("amount") == null ? 2 : Integer.parseInt(e.getAttributeValue("amount")));
            model.setInit(e.getAttributeValue("init") == null || Boolean.getBoolean(e.getAttributeValue("init")));
            model.setSql(((Element) e.getChildren().get(0)) .getText());


            if (model.isInit()) 
{
                cache.put(key, initTCValues(model));
            }
            sqlMap.put(key, model);
        }
    }


    /** *//**
     * @param file
     * @return Element
     */

    private static Element getRootElement(File file) 
{

        try 
{
            SAXBuilder saxbuilder = new SAXBuilder();
            Document document = saxbuilder.build(file);
            lastModefied = file.lastModified();
            return document.getRootElement();

        } catch (JDOMException e) 
{
            throw new RuntimeException("JDOMException:" + e.getMessage());
        }
    }


    /** *//**
     * @return File
     */

    private static File getFile() 
{
        File file = new File(ClassLoaderUtil.getResource(CONFIG_FILE, TCHelper.class).getFile());

        if (!file.exists()) 
{
            file = new File(TCHelper.class.getResource(CONFIG_FILE).getFile());
            if (!file.exists())
                throw new RuntimeException(CONFIG_FILE + " file not exists");
        }

        return file;
    }


    /** *//**
     * @param model
     * @return List
     */

    private static List initTCValues(TCModel model) 
{

        try 
{
            //logger.info("model:" + model);
            return QueryManager.excuteSql(model.getSql());

        } catch (SQLException e) 
{
            logger.error("SQLException:" + e.getMessage());
            return null;
        }
    }


    /** *//**
     * @return boolean
     */

    private static boolean reload() 
{
        File file = getFile();
        return lastModefied != file.lastModified();
    }


    /** *//**
     * @return List
     */

    private static List emptyList() 
{
        return new ArrayList();
    }


    /** *//**
     * @param key
     * @return List
     */

    private static List getValue(String key) 
{
        return (List) cache.get(key);
    }


    /** *//**
     * 字典表配置文件对应的Model
     */

    protected static class TCModel 
{
        private String id;
        private int amount;
        private boolean init;
        private String sql;


        public String getId() 
{
            return id;
        }


        public void setId(String id) 
{
            this.id = id;
        }


        public int getAmount() 
{
            return amount;
        }


        public void setAmount(int amount) 
{
            this.amount = amount;
        }


        public boolean isInit() 
{
            return init;
        }


        public void setInit(boolean init) 
{
            this.init = init;
        }


        public String getSql() 
{
            return sql;
        }


        public void setSql(String sql) 
{
            this.sql = sql;
        }

        public String toString() 
{
            return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
        }
    }
} 3)xml
我们的xml配置如下:
<?xml version="1.0" encoding="GB2312"?>

<sqlquery>

    <query id="tc_reg_workmode">
        <sql><![CDATA[
         select t.workmodecode, t.workmodename
        from TC_Reg_WorkMode t
        where t.choiceflag = '1'
        ]]>sql>
    query>
    
    <query id="tc_pub_taxorgdept" init="false">
        <sql><![CDATA[
        select t.orgdeptcode,t.orgdeptname from tc_pub_taxorgdept t where t.auditflag='1' and t.orgdeptcode like ?
        ]]>sql>
    query>    
sqlquery>
end