在PreparedStatementPool中,成员变量除了日志外,只有两项,一项为LRUCache类型的map,另一项为DruidAbstractDataSource的dataSource,其中map属性为已缓存语句的存储结构,dataSource为当前连接池的指向。
public class PreparedStatementPool {
private final static Log LOG = LogFactory.getLog(PreparedStatementPool.class);
private final LRUCache map;
private final DruidAbstractDataSource dataSource;
}
1.1 LRUCache
LRUCache是继承自LinkedHashMap来实现的LRU缓存,其重写了LinkedHashMap中的removeEldestEntry()方法,该方法实现了对最近最少使用的语句进行回收操作。
public class LRUCache extends LinkedHashMap1.2 PreparedStatementKey{ private static final long serialVersionUID = 1L; public LRUCache(int maxSize){ //调用父类构造方法,初始化大小为druid.maxPoolPreparedStatementPerConnectionSize参数配置项,加载因子为0.75,默认按照访问顺序进行排序 super(maxSize, 0.75f, true); } protected boolean removeEldestEntry(Entry eldest) { //如果当前语句池中语句数量大于配置的最大数,则关闭并移除该语句 boolean remove = (size() > dataSource.getMaxPoolPreparedStatementPerConnectionSize()); if (remove) { closeRemovedStatement(eldest.getValue()); } return remove; } }
在判断PreparedStatementKey是否一致时,需要所有字段一致才可以
public static class PreparedStatementKey {
protected final String sql;
protected final String catalog;
protected final MethodType methodType;
public final int resultSetType;
public final int resultSetConcurrency;
public final int resultSetHoldability;
public final int autoGeneratedKeys;
//列映射
private final int[] columnIndexes;
private final String[] columnNames;
}
1.3 PreparedStatementHolder
以default开头的选项为默认配置,在语句执行完被回收时,将相关参数进行重置
inUseCount用来判断当前语句是否在被使用,如果被使用中则不允许被回收
pooling在开启PSCache选项时,默认为true,在进行回收时会设置为false
public final class PreparedStatementHolder {
public final PreparedStatementKey key;
public final PreparedStatement statement;
private int hitCount = 0;
private int fetchRowPeak = -1;
private int defaultRowPrefetch = -1;
private int rowPrefetch = -1;
private boolean enterOracleImplicitCache = false;
private int inUseCount = 0;
private boolean pooling = false;
}
2.PreparedStatementPool对已缓存的语句处理
2.1 从语句池中取出语句(get方法)
public PreparedStatementHolder get(PreparedStatementKey key) throws SQLException {
//从LRU缓存中获取
PreparedStatementHolder holder = map.get(key);
//判空
if (holder != null) {
//如果当前语句正在使用中并且未设置sharePreparedStatements,则不返回内容
if (holder.isInUse() && (!dataSource.isSharePreparedStatements())) {
return null;
}
//增加缓存命中计数,监控指标
holder.incrementHitCount();
//增加连接池中的缓存命中计数,监控指标
dataSource.incrementCachedPreparedStatementHitCount();
//如果是Oracle数据库,将缓存状态变更到活动
if (holder.isEnterOracleImplicitCache()) {
OracleUtils.exitImplicitCacheToActive(holder.statement);
}
} else {
//增加链接池中未命中缓存数,监控指标
dataSource.incrementCachedPreparedStatementMissCount();
}
return holder;
}
2.2 置入OR归还语句(put方法)
public void put(PreparedStatementHolder stmtHolder) throws SQLException {
//获得实际的语句对象
PreparedStatement stmt = stmtHolder.statement;
//如果实际对象为空,不处理
if (stmt == null) {
return;
}
//如果是Oracle数据库并且使用了Oracle的隐式缓存
if (dataSource.isOracle() && dataSource.isUseOracleImplicitCache()) {
//更改当前的游标状态
OracleUtils.enterImplicitCache(stmt);
//设置Oracle隐式缓存状态
stmtHolder.setEnterOracleImplicitCache(true);
} else {
//设置未使用Oracle隐式缓存
stmtHolder.setEnterOracleImplicitCache(false);
}
//将当前语句置入语句池中
PreparedStatementHolder oldStmtHolder = map.put(stmtHolder.key, stmtHolder);
//如果之前的引用地址和现在的一致,则不继续处理内容(可以认为是一种归还)
if (oldStmtHolder == stmtHolder) {
return;
}
//如果有返回旧语句,则将旧语句回收并关闭
if (oldStmtHolder != null) {
oldStmtHolder.setPooling(false);
closeRemovedStatement(oldStmtHolder);
} else {
//如果当前语句持有者没有命中过缓存,则认为是新的一条缓存,需要增加连接池中已缓存语句数
if (stmtHolder.getHitCount() == 0) {
dataSource.incrementCachedPreparedStatementCount();
}
}
//设置当前语句在池中的状态
stmtHolder.setPooling(true);
if (LOG.isDebugEnabled()) {
String message = null;
if (stmtHolder.statement instanceof PreparedStatementProxy) {
PreparedStatementProxy stmtProxy = (PreparedStatementProxy) stmtHolder.statement;
if (stmtProxy instanceof CallableStatementProxy) {
message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
+ "} enter cache";
} else {
message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
+ "} enter cache";
}
} else {
message = "stmt enter cache";
}
LOG.debug(message);
}
}
2.3 关闭并删除语句(closeRemovedStatement)
该方法为对缓存语句的回收关闭方法,在对缓存语句弃用时会被调用
public void closeRemovedStatement(PreparedStatementHolder holder) {
if (LOG.isDebugEnabled()) {
String message = null;
if (holder.statement instanceof PreparedStatementProxy) {
PreparedStatementProxy stmtProxy = (PreparedStatementProxy) holder.statement;
if (stmtProxy instanceof CallableStatementProxy) {
message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
+ "} exit cache";
} else {
message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
+ "} exit cache";
}
} else {
message = "stmt exit cache";
}
LOG.debug(message);
}
//将语句在池中的状态设为false,软删除
holder.setPooling(false);
//如果当前语句正在使用,则暂不处理
if (holder.isInUse()) {
return;
}
//如果当前开启了Oracle的隐式缓存
if (holder.isEnterOracleImplicitCache()) {
try {
//Oracle特殊的关闭方式
OracleUtils.exitImplicitCacheToClose(holder.statement);
} catch (Exception ex) {
LOG.error("exitImplicitCacheToClose error", ex);
}
}
//调用连接池中方法进行关闭
dataSource.closePreapredStatement(holder);
}
public void closePreapredStatement(PreparedStatementHolder stmtHolder) {
//如果当前持有者为空,不处理
if (stmtHolder == null) {
return;
}
//增加已关闭语句缓存计数,监控指标
closedPreparedStatementCountUpdater.incrementAndGet(this);
//减少已缓存语句计数,监控指标
decrementCachedPreparedStatementCount();
//增加已删除一句计数,监控指标
incrementCachedPreparedStatementDeleteCount();
//使用JDBC工具类对语句进行关闭
JdbcUtils.close(stmtHolder.statement);
}



