gu.sql2java.druid.DruidDataSourceFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sql2java-manager Show documentation
Show all versions of sql2java-manager Show documentation
sql2java manager class package for accessing database
package gu.sql2java.druid;
import java.io.Closeable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import gu.sql2java.DataSourceConfig;
import gu.sql2java.DataSourceFactory;
import gu.sql2java.utils.CaseSupport;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
import static gu.sql2java.SimpleLog.log;
import static gu.sql2java.DataSourceConfig.logDatabaseProperties;
import static com.alibaba.druid.pool.DruidDataSourceFactory.PROP_URL;
import static com.alibaba.druid.pool.DruidDataSourceFactory.PROP_DRIVERCLASSNAME;
import static com.alibaba.druid.pool.DruidDataSourceFactory.PROP_USERNAME;
import static com.alibaba.druid.pool.DruidDataSourceFactory.PROP_PASSWORD;
/**
* 基于druid实现{@link DataSourceFactory}接口
* @author guyadong
*
*/
public class DruidDataSourceFactory implements DataSourceFactory{
private static final String jdbcPrefix = "jdbc.";
private static final String c3p0Prefix = "c3p0.";
private static final String springDatasourcePrefix = "spring.datasource.";
private static final String springDatasourceDruidPrefix = "spring.datasource.druid.";
private static final String druidPrefix = "druid.";
private static final String driverSuffix = ".driver";
/** {@link com.alibaba.druid.pool.DruidDataSourceFactory}所有属性名 */
private static final Set druidProperyNames = Sets.newHashSet(getDuridPropertyNames().values());
public DruidDataSourceFactory() {
}
/**
* if not contain sub key without prefix,rename key to sub key without prefix ,and convert to camel-case string
*/
private static boolean renameKey(Properties props,String key,String prefix){
return renameKey(props,key,prefix,null);
}
/**
* if not contain sub key without prefix,rename key to sub key without prefix ,and convert to camel-case string
* @param props
* @param key
* @param prefix
* @param newPrefix new prefix if not empty or null
*/
private static boolean renameKey(Properties props,String key,String prefix,String newPrefix){
if(key.startsWith(prefix)){
Object value = props.remove(key);
String subKey = key.substring(prefix.length());
String camelcaseKey = subKey;
if(Pattern.compile("[-_]").matcher(subKey).find()){
camelcaseKey = CaseSupport.toCamelcase(subKey.replace('-', '_').toLowerCase());
}
/** 统一改为驼峰命名(camel-case) */
if(!props.containsKey(camelcaseKey) && !props.containsKey(nullToEmpty(newPrefix) + camelcaseKey)){
props.put(nullToEmpty(newPrefix) + camelcaseKey, value);
}
return true;
}
return false;
}
private static void replaceKey(Properties props,String from,String to){
if(props.containsKey(from)){
/** 对于命名格式不匹配的参数改名 */
Object value = props.remove(from);
props.put(to, value);
}else if(props.containsKey(druidPrefix + from)){
/** 对于命名格式不匹配的参数改名 */
Object value = props.remove(druidPrefix + from);
props.put(to, value);
}
}
/**
* 归一化{@link Properties}配置参数
* 如果参数名有{@code prefix}前缀,则将参数前缀改为{@link newPrefix},参数名改为驼峰命名,
* 如果参数名有 c3p0.前缀则将参数删除,
* 将driver参数改名为driverClassName,
*/
private static void normalizePropertiesForDurid0(Properties props,String prefix,String newPrefix){
props.stringPropertyNames().forEach(k->{
if(k.startsWith(prefix)){
/** rename key ,remove jdbc. */
renameKey(props, k, prefix,newPrefix);
}else if (k.startsWith(c3p0Prefix)){
/** remove key with prefix c3p0. */
props.remove(k);
return ;
}
if(k.endsWith(driverSuffix)){
/** rename .driver to .driverClassName */
Object value = props.remove("driver");
props.put(PROP_DRIVERCLASSNAME, value);
}
});
}
/**
* 将{@link Properties}配置归一化为 Druid 可识别的配置参数
* 如果参数名有 jdbc.,spring.datasource.druid.,spring.datasource.,druid.前缀,则将参数改名(camel-case)删除前缀
* 如果参数名有 c3p0.前缀,或非单级参数(包含.)则将参数删除
* 将driver参数改名为driverClassName
* 删除所有非druid参数名
*/
static void normalizePropertiesForDurid(Properties props){
normalizePropertiesForDurid0(props,jdbcPrefix,null);
normalizePropertiesForDurid0(props,druidPrefix,druidPrefix);
normalizePropertiesForDurid0(props,springDatasourceDruidPrefix,druidPrefix);
normalizePropertiesForDurid0(props,springDatasourcePrefix,null);
/** 检查props中的参数名 */
druidProperyNames.forEach(n->{
if(props.containsKey(n)){
// DO NOTHING
}else if(props.containsKey(druidPrefix + n)){
renameKey(props, druidPrefix + n, druidPrefix);
}else if(n.startsWith(druidPrefix)){
// DO NOTHING
}else if(Pattern.compile("[-_]").matcher(n).find()){
/** 转为驼峰命名尝试查找 */
String camelcaseKey = CaseSupport.toCamelcase(n.replace('-', '_').toLowerCase());
replaceKey(props,camelcaseKey,n);
}else{
/** 转为鱼骨命名尝试查找 */
String spinalcaseKey = CaseSupport.toSnakecase(n).replace('_', '-');
replaceKey(props,spinalcaseKey,n);
}
});
props.stringPropertyNames().forEach(k->{
if (k.indexOf('.') >= 0 && !k.startsWith(druidPrefix)){
/** remove any key with dot */
props.remove(k);
return ;
}
});
}
/**
* 从{@link com.alibaba.druid.pool.DruidDataSourceFactory}获取所有PROP_前缀的静态常量值
*
* - K --- 常量字段名
* - V --- 常量字段值(DRUID配置属性名)
*
*/
private static Map getDuridPropertyNames(){
Iterable constFields = Iterables.filter(Arrays.asList(com.alibaba.druid.pool.DruidDataSourceFactory.class.getFields()),
f->f.getName().startsWith("PROP_") && f.getType().equals(String.class) && Modifier.isStatic(f.getModifiers()) && Modifier.isFinal(f.getModifiers()));
Map duridConstPopertyNames = Maps.newHashMap();
constFields.forEach(f->{
try {
duridConstPopertyNames.put(f.getName(), (String)f.get(null));
} catch (IllegalArgumentException | IllegalAccessException e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
});
return duridConstPopertyNames;
}
/**
* 根据{@link Properties}提供的参数创建{@link DataSource}实例
* @param input
*/
public static DataSource createDataSource(Properties input){
Properties props = new Properties();
props.putAll(checkNotNull(input,"props is null"));
try{
normalizePropertiesForDurid(props);
checkArgument(props.containsKey(PROP_URL),"MISSING ESSENTIAL ARGUMENT %s",PROP_URL);
logDatabaseProperties(props, "druid");
return com.alibaba.druid.pool.DruidDataSourceFactory.createDataSource(props);
}catch (Exception e){
String message = String.format("can't get connection by argument...url/username/password[%s/%s/%s]",
props.getProperty(PROP_URL),props.getProperty(PROP_USERNAME),props.getProperty(PROP_PASSWORD));
throw new IllegalArgumentException(message,e);
}
}
@Override
public DataSource createDataSource(DataSourceConfig config){
Properties props = new Properties();
props.putAll(checkNotNull(config,"config is null").getInitProperties());
return createDataSource(props);
}
@Override
public void destroy(DataSource dataSource){
try{
if(dataSource instanceof Closeable){
((Closeable)dataSource).close();
}
}catch (Exception e) {
log("dispose DruidDataSource wrong ..." + e);
}
}
}