All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nutz.plugins.cache.dao.CachedNutDaoExecutor Maven / Gradle / Ivy

There is a newer version: 1.r.69.v20220215
Show newest version
package org.nutz.plugins.cache.dao;

import java.sql.Connection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.nutz.dao.DB;
import org.nutz.dao.DaoException;
import org.nutz.dao.DatabaseMeta;
import org.nutz.dao.impl.sql.run.NutDaoExecutor;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.DaoStatement;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.plugins.cache.dao.api.DaoCacheProvider;
import org.nutz.trans.Trans;

import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser;
import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser;
import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser;
import com.alibaba.druid.sql.parser.SQLStatementParser;

/**
 * 基于sql的缓存DaoExecutor. 使用Druid的sql处理器. 要配置需要缓存的表及数据库类型!!!!
 * 
 * @author wendal([email protected])
 *
 */
public class CachedNutDaoExecutor extends NutDaoExecutor {

    /**
     * 缓存实现提供者,默认是MemoryDaoCacheProvider
     */
    protected DaoCacheProvider cacheProvider;

    /**
     * 在事务环境下是否启动,默认禁用
     */
    protected boolean enableWhenTrans;

    /**
     * 禁止清除缓存的标志,在Sql.getContext()中配置
     */
    protected String cacheClearMark = "dao-cache-clear";
    
    /**
     * 跳过缓存的标记
     */
    public static final String CacheSkipMark = "dao-cache-skip";

    /**
     * 需要缓存的数据库表
     */
    protected Set cachedTableNames = new HashSet();

    /**
     * 需要缓存的数据库表名称的正则表达式
     */
    protected Pattern cachedTableNamePatten;

    /**
     * 是否打印详细的log,默认为关
     */
    public static boolean DEBUG = false;

    /**
     * 是否缓存空值
     */
    protected boolean cache4Null = true;

    /**
     * 是否启用,全局开关,默认为true
     */
    protected boolean enable = true;

    /**
     * 数据库类型,当前仅支持 MYSQL, ORACLE, PSQL, 默认MYSQL
     */
    protected DB db = DB.MYSQL;

    private static final Log log = Logs.get();

    public void exec(Connection conn, DaoStatement st) {
        if (!enable || ("true".equals(st.getContext().attr(CacheSkipMark)))) {
            super.exec(conn, st);
            return;
        }
        String prepSql = st.toPreparedStatement();
        if (prepSql == null) {
            super.exec(conn, st);
            return;
        }
        SQLStatementParser parser = sqlParser(prepSql);
        List statementList = null;
        try {
            statementList = parser.parseStatementList();
        }
        catch (Exception e) {
            log.debug("parser SQL sql, skip cache detect!! SQL=" + prepSql);
            super.exec(conn, st);
            return;
        }
        if (statementList.size() != 1) {
            log.warn("more than one sql in one DaoStatement!! skip cache detect!! SQL=" + prepSql);
            super.exec(conn, st);
            return;
        }
        SQLStatement sqlStatement = statementList.get(0);
        if (sqlStatement == null) {
            log.warn("can't parse SQL !! skip cache detect!! SQL=" + prepSql);
            super.exec(conn, st);
            return;
        }
        // 检查需要执行的sql
        NSqlAdapter adapter = new NSqlAdapter();
        sqlStatement.accept(adapter); // 得到将会操作的表
        List tableNames = adapter.tableNames;
        if (DEBUG)
            log.debug("sql = " + prepSql + ", tables = " + tableNames);
        if (sqlStatement instanceof SQLSelectStatement) {
            // 如果是select且不是batch(参数表只有一行,那么可能是缓存哦)
            Object[][] params = st.getParamMatrix();
            if (Trans.isTransactionNone() || enableWhenTrans) {
                if (tableNames.size() == 1
                    && isCache4Table(tableNames.get(0))
                    && params.length <= 1) {
                    String tableName = tableNames.get(0);
                    String key = genKey(st, prepSql, params);
                    if (DEBUG)
                        log.debugf("KEY=%s SQL=%s", key, prepSql);
                    Object cachedValue = getCacheProvider().get(genCacheName(tableName), key);
                    if (cachedValue != null && !(CacheResult.NOT_FOUNT.equals(cachedValue))) {
                        if (CacheResult.NULL.equals(cachedValue))
                            cachedValue = null;
                        if (DEBUG)
                            log.debug("cache found key=" + key);
                        st.getContext().setResult(cachedValue);
                    } else {
                        if (DEBUG)
                            log.debug("cache miss = " + prepSql);
                        super.exec(conn, st);
                        cachedValue = st.getContext().getResult();
                        if (cachedValue != null || cache4Null)
                            getCacheProvider().put(genCacheName(tableName), key, cachedValue);
                    }
                    return;
                } else {
                    if (DEBUG)
                        log.debug("not good for cache >> " + prepSql);
                }
            }
            tableNames.clear(); // Select的表可别清除了
        } else {
            Object mark = st.getContext().attr(cacheClearMark);
            if (mark != null && (Boolean) mark)
                tableNames.clear();
        }
        try {
            super.exec(conn, st);
        }
        finally {
            try {
                if (!tableNames.isEmpty()) {
                    for (String tableName : tableNames) {
                        if (DEBUG)
                            log.debug("Clear Cache=" + tableName);
                        getCacheProvider().clear(genCacheName(tableName));
                    }
                }
            }
            catch (Throwable e) {
                log.warn("clear cache fail: " + tableNames, e);
            }
        }
    }

    /**
     * 缓存key的生成机制,默认是 hash:pagerNum:pagerSize:sql:param1:param2:....
     * 

* 子类可覆盖本方法实现更有效的key生成 */ protected String genKey(DaoStatement st, String prepareSql, Object[][] params) { StringBuilder sb = new StringBuilder(); long hash = prepareSql.hashCode(); Pager pager = st.getContext().getPager(); if (pager != null) { sb.append("" + pager.getPageNumber() + ":" + pager.getPageSize() + ":"); } else { sb.append("_:_:"); } sb.append(prepareSql); if (params != null && params.length > 0 && params[0].length > 0) { for (Object param : params[0]) { String v = String.valueOf(param); sb.append(":").append(v); hash += v.hashCode(); } } return hash + ":" + sb.toString(); } /** * 生成特定Cache名. 子类可覆盖实现所需要的Cache名 */ protected String genCacheName(String tableName) { return tableName; } /** * 根据数据库类型解析sql */ protected SQLStatementParser sqlParser(String sql) { switch (db) { case MYSQL: return new MySqlStatementParser(sql); case ORACLE: return new OracleStatementParser(sql); case PSQL: return new PGSQLStatementParser(sql); case SQLSERVER: return new SQLServerStatementParser(sql); case DB2: return new DB2StatementParser(sql); default: throw new DaoException("daocache not support at this database"); } } public void setCacheProvider(DaoCacheProvider cacheProvider) { this.cacheProvider = cacheProvider; } public void setEnableWhenTrans(boolean enableWhenTrans) { this.enableWhenTrans = enableWhenTrans; } public void setCachedTableNames(Set cachedTableNames) { this.cachedTableNames = cachedTableNames; } public void addCachedTableName(String name) { this.cachedTableNames.add(name); } public void setCachedTableNamePatten(Pattern cachedTableNamePatten) { this.cachedTableNamePatten = cachedTableNamePatten; } public void setCachedTableNamePatten(String cachedTableNamePatten) { if (cachedTableNamePatten == null) this.cachedTableNamePatten = null; else this.cachedTableNamePatten = Pattern.compile(cachedTableNamePatten); } /** * 是否对表进行缓存. 子类可以扩展该方法实现更复杂的配置 */ protected boolean isCache4Table(String tableName) { return this.cachedTableNames.contains(tableName) || (cachedTableNamePatten != null && cachedTableNamePatten.matcher(tableName).find()); } public DaoCacheProvider getCacheProvider() { if (cacheProvider == null) throw new IllegalArgumentException("Need CacheProvider!!"); return cacheProvider; } public void setCache4Null(boolean cache4Null) { this.cache4Null = cache4Null; } public void setEnable(boolean enable) { this.enable = enable; if (!this.enable) { log.info("CachedNutDaoExecutor will disable."); } } public void setMeta(DatabaseMeta meta) { this.db = meta.getType(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy