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

com.jfinal.plugin.druid.DruidPlugin Maven / Gradle / Ivy

/**
 * Copyright (c) 2011-2023, James Zhan 詹波 ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jfinal.plugin.druid;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.IDataSourceProvider;

/**
 * DruidPlugin.
 */
public class DruidPlugin implements IPlugin, IDataSourceProvider {
	//连接池的名称
	protected String name = null;
	
	// 基本属性 url、user、password
	protected String url;
	protected String username;
	protected String password;
	protected String publicKey;
	protected String driverClass = null;	// 由 "com.mysql.jdbc.Driver" 改为 null 让 druid 自动探测 driverClass 值
	
	// 初始连接池大小、最小空闲连接数、最大活跃连接数
	protected int initialSize = 1;
	protected int minIdle = 10;
	protected int maxActive = 32;
	
	// 配置获取连接等待超时的时间
	protected long maxWait = DruidDataSource.DEFAULT_MAX_WAIT;
	
	// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
	protected long timeBetweenEvictionRunsMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
	// 配置连接在池中最小生存的时间
	protected long minEvictableIdleTimeMillis = DruidDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
	// 配置发生错误时多久重连
	protected long timeBetweenConnectErrorMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS;
	
	/**
	 * hsqldb - "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"
	 * Oracle - "select 1 from dual"
	 * DB2 - "select 1 from sysibm.sysdummy1"
	 * mysql - "select 1"
	 */
	protected String validationQuery = "select 1";
	protected String  connectionInitSql = null;
	protected String connectionProperties = null;
	protected boolean testWhileIdle = true;
	protected boolean testOnBorrow = false;
	protected boolean testOnReturn = false;
	
	// 是否打开连接泄露自动检测
	protected boolean removeAbandoned = false;
	// 连接长时间没有使用,被认为发生泄露时长
	protected long removeAbandonedTimeoutMillis = 300 * 1000;
	// 发生泄露时是否需要输出 log,建议在开启连接泄露检测时开启,方便排错
	protected boolean logAbandoned = false;
	
	// 是否缓存preparedStatement,即PSCache,对支持游标的数据库性能提升巨大,如 oracle、mysql 5.5 及以上版本
	// protected boolean poolPreparedStatements = false;	// oracle、mysql 5.5 及以上版本建议为 true;
	
	// 只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true,使用oracle时可以设定此值。
	protected int maxPoolPreparedStatementPerConnectionSize = -1;
	
	protected Integer defaultTransactionIsolation = null;
	protected Integer validationQueryTimeout = null;
	protected Integer timeBetweenLogStatsMillis = null;
	protected Boolean keepAlive = null;
	
	// 配置监控统计拦截的filters
	protected String filters;	// 监控统计:"stat"    防SQL注入:"wall"     组合使用: "stat,wall"
	protected List filterList;
	
	protected DruidDataSource ds;
	protected volatile boolean isStarted = false;
	
	public DruidPlugin(String url, String username, String password) {
		this.url = url;
		this.username = username;
		this.password = password;
		this.validationQuery = autoCheckValidationQuery(url);
	}
	
	public DruidPlugin(String url, String username, String password, String driverClass) {
		this.url = url;
		this.username = username;
		this.password = password;
		this.driverClass = driverClass;
		this.validationQuery = autoCheckValidationQuery(url);
	}
	
	public DruidPlugin(String url, String username, String password, String driverClass, String filters) {
		this.url = url;
		this.username = username;
		this.password = password;
		this.driverClass = driverClass;
		this.filters = filters;
		this.validationQuery = autoCheckValidationQuery(url);
	}
	
	/**
	 * @Title: autoCheckValidationQuery  
	 * @Description: 自动设定探测sql 
	 * @param url
	 * @return 
	 * @since V1.0.0
	 */
	private static String  autoCheckValidationQuery(String url){
		if(url.startsWith("jdbc:oracle")){
			return "select 1 from dual";
		}else if(url.startsWith("jdbc:db2")){
			return "select 1 from sysibm.sysdummy1";
		}else if(url.startsWith("jdbc:hsqldb")){
			return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS";
		}else if(url.startsWith("jdbc:derby")){
			return "values (1)";
		}
		return "select 1";
	}
	
	/**
	 * 添加连接时的初始化sql。可以添加多次,在初次连接时使用,比如指定编码或者默认scheme等
	 * @param sql
	 */
	public void setConnectionInitSql(String sql){
		this.connectionInitSql = sql;
	}
	
	public final String getName() {
		return name;
	}

	/**
	 * 连接池名称
	 *
	 * @param name
	 */
	public final void setName(String name) {
		this.name = name;
	}

	/**
	 * 设置过滤器,如果要开启监控统计需要使用此方法或在构造方法中进行设置
	 * 

* 监控统计:"stat" * 防SQL注入:"wall" * 组合使用: "stat,wall" *

*/ public DruidPlugin setFilters(String filters) { this.filters = filters; return this; } public synchronized DruidPlugin addFilter(Filter filter) { if (filterList == null) filterList = new ArrayList(); filterList.add(filter); return this; } public boolean start() { if (isStarted) return true; ds = new DruidDataSource(); if(this.name != null){ ds.setName(this.name); } ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); if (driverClass != null) ds.setDriverClassName(driverClass); ds.setInitialSize(initialSize); ds.setMinIdle(minIdle); ds.setMaxActive(maxActive); ds.setMaxWait(maxWait); ds.setTimeBetweenConnectErrorMillis(timeBetweenConnectErrorMillis); ds.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); ds.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); ds.setValidationQuery(validationQuery); if(StrKit.notBlank(connectionInitSql)){ List connectionInitSqls = new ArrayList(); connectionInitSqls.add(this.connectionInitSql); ds.setConnectionInitSqls(connectionInitSqls); } ds.setTestWhileIdle(testWhileIdle); ds.setTestOnBorrow(testOnBorrow); ds.setTestOnReturn(testOnReturn); ds.setRemoveAbandoned(removeAbandoned); ds.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis); ds.setLogAbandoned(logAbandoned); //只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true,参照druid的源码 ds.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); if (defaultTransactionIsolation != null) { ds.setDefaultTransactionIsolation(defaultTransactionIsolation); } if (validationQueryTimeout != null) { ds.setValidationQueryTimeout(validationQueryTimeout); } if (timeBetweenLogStatsMillis != null) { ds.setTimeBetweenLogStatsMillis(timeBetweenLogStatsMillis); } if (keepAlive != null) { ds.setKeepAlive(keepAlive); } boolean hasSetConnectionProperties = false; if (StrKit.notBlank(filters)){ try { ds.setFilters(filters); //支持加解密数据库 if(filters.contains("config")){ //判断是否设定了公钥 if(StrKit.isBlank(this.publicKey)){ throw new RuntimeException("Druid连接池的filter设定了config时,必须设定publicKey"); } String decryptStr = "config.decrypt=true;config.decrypt.key="+this.publicKey; String cp = this.connectionProperties; if(StrKit.isBlank(cp)){ cp = decryptStr; }else{ cp = cp + ";" + decryptStr; } ds.setConnectionProperties(cp); hasSetConnectionProperties = true; } } catch (SQLException e) {throw new RuntimeException(e);} } //确保setConnectionProperties被调用过一次 if(!hasSetConnectionProperties && StrKit.notBlank(this.connectionProperties)){ ds.setConnectionProperties(this.connectionProperties); } addFilterList(ds); isStarted = true; return true; } private void addFilterList(DruidDataSource ds) { if (filterList != null) { List targetList = ds.getProxyFilters(); for (Filter add : filterList) { boolean found = false; for (Filter target : targetList) { if (add.getClass().equals(target.getClass())) { found = true; break; } } if (! found) targetList.add(add); } } } public boolean stop() { if (ds != null) ds.close(); ds = null; isStarted = false; return true; } public DataSource getDataSource() { return ds; } /** * 支持高版本 druid 下配置 connectTimeout、socketTimeout。使用方法如下: * druidPlugin.getDruidDataSource().setConnectTimeout(xxx); * druidPlugin.getDruidDataSource().setSocketTimeout(xxx); */ public DruidDataSource getDruidDataSource() { return ds; } public DruidPlugin set(int initialSize, int minIdle, int maxActive) { this.initialSize = initialSize; this.minIdle = minIdle; this.maxActive = maxActive; return this; } public DruidPlugin setDriverClass(String driverClass) { this.driverClass = driverClass; return this; } public DruidPlugin setInitialSize(int initialSize) { this.initialSize = initialSize; return this; } public DruidPlugin setMinIdle(int minIdle) { this.minIdle = minIdle; return this; } public DruidPlugin setMaxActive(int maxActive) { this.maxActive = maxActive; return this; } public DruidPlugin setMaxWait(long maxWait) { this.maxWait = maxWait; return this; } public DruidPlugin setDefaultTransactionIsolation(int defaultTransactionIsolation) { this.defaultTransactionIsolation = defaultTransactionIsolation; return this; } public DruidPlugin setValidationQueryTimeout(int validationQueryTimeout) { this.validationQueryTimeout = validationQueryTimeout; return this; } public DruidPlugin setTimeBetweenLogStatsMillis(int timeBetweenLogStatsMillis) { this.timeBetweenLogStatsMillis = timeBetweenLogStatsMillis; return this; } public DruidPlugin setKeepAlive(boolean keepAlive) { this.keepAlive = keepAlive; return this; } public DruidPlugin setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; return this; } public DruidPlugin setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; return this; } /** * hsqldb - "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS" * Oracle - "select 1 from dual" * DB2 - "select 1 from sysibm.sysdummy1" * mysql - "select 1" */ public DruidPlugin setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; return this; } public DruidPlugin setTestWhileIdle(boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; return this; } public DruidPlugin setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; return this; } public DruidPlugin setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; return this; } public DruidPlugin setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) { this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; return this; } public final DruidPlugin setTimeBetweenConnectErrorMillis(long timeBetweenConnectErrorMillis) { this.timeBetweenConnectErrorMillis = timeBetweenConnectErrorMillis; return this; } public final DruidPlugin setRemoveAbandoned(boolean removeAbandoned) { this.removeAbandoned = removeAbandoned; return this; } public final DruidPlugin setRemoveAbandonedTimeoutMillis(long removeAbandonedTimeoutMillis) { this.removeAbandonedTimeoutMillis = removeAbandonedTimeoutMillis; return this; } public final DruidPlugin setLogAbandoned(boolean logAbandoned) { this.logAbandoned = logAbandoned; return this; } public final DruidPlugin setConnectionProperties(String connectionProperties) { this.connectionProperties = connectionProperties; return this; } public final DruidPlugin setPublicKey(String publicKey) { this.publicKey = publicKey; return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy