最近开发项目使用Ibatis+Lucene,使用轻量框架实例自己管理,不使用Spring,Ibatis数据源原来使用JNDI,为统一配置加入dbconfig.properties,并使用boneCP,Ibatis2数据源只提供JNDI、SIMPLE、DBCP,无法对BoneCP提供支持,故参考DbcpDataSourceFactory自己创建工厂类,发布博客,以作备份.
dbconfig.properties
dbconfig.properties
###============搜索数据库配置=============##
search.db.url=jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST =*)(PORT = 1521)))(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = wcsdb)))
search.db.username=xiu_search
search.db.password=*
##bonecp
##每60秒检查所有连接池中的空闲连接
search.db.idleConnectionTestPeriod=20
##设置连接空闲时间
search.db.idleMaxAge=20
##设置连接池在每个分区中的最大连接数
search.db.maxConnectionsPerPartition=15
##设置连接池设在每个分区中的最小连接数
search.db.minConnectionsPerPartition=10
##设置分区(设置 2个分区)
search.db.partitionCount=2
##连接池中的连接耗尽的时候 BoneCP一次同时获取的连接数
search.db.acquireIncrement=5
##每个分区释放链接助理进程的数量,默认值:3,除非你的一个数据库连接的时间内做了很多工作,不然过多的助理进程会影响你的性能
search.db.releaseHelperThreads=3
search.db.statementsCachedPerConnection=100
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<properties resource="dbconfig.properties"/>
<settings cacheModelsEnabled="true" enhancementEnabled="true"
lazyLoadingEnabled="true" maxRequests="10" maxSessions="20"
maxTransactions="50" useStatementNamespaces="true"
defaultStatementTimeout="30" statementCachingEnabled="true"
classInfoCacheEnabled="true" errorTracingEnabled="true" />
<typeAlias alias="BONECP" type="com.pltfm.sys.util.BoneCPDataSourceFactory"/>
<transactionManager type="JDBC" commitRequired="false">
<dataSource type="BONECP">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl" value="${search.db.url}" />
<property name="username" value="${search.db.username}" />
<property name="password" value="${search.db.password}" />
<property name="idleMaxAge" value="${search.db.idleMaxAge}" />
<property name="partitionCount" value="${search.db.partitionCount}" />
<property name="maxConnectionsPerPartition" value="${search.db.maxConnectionsPerPartition}" />
<property name="minConnectionsPerPartition" value="${search.db.minConnectionsPerPartition}" />
<property name="driver.encoding" value="UTF8" />
<property name="Driver.releaseHelperThreads" value="${search.db.releaseHelperThreads}" />
<property name="Driver.statementsCachedPerConnection" value="${search.db.statementsCachedPerConnection}" />
</dataSource>
</transactionManager>
<sqlMap resource="com/pltfm/sys/sqlmap/sys_param_SqlMap.xml" />
</sqlMapConfig>
package com.pltfm.sys.util;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import com.ibatis.common.beans.Probe;
import com.ibatis.common.beans.ProbeFactory;
import com.ibatis.sqlmap.engine.datasource.DataSourceFactory;
import com.jolbox.bonecp.BoneCPDataSource;
/**
* <h1>支持Ibatis2 BoneCP连接池工厂</h2><br/>
* <h2>默认提供参数如下:</h2><br/>
* #driverClass<br/>
* #jdbcUrl<br/>
* #username<br/>
* #password<br/>
* #每60秒检查所有连接池中的空闲连接
* idleConnectionTestPeriod=20<br/>
* ##设置连接空闲时间<br/>
* idleMaxAge=20<br/>
* ##设置连接池在每个分区中的最大连接数<br/>
* maxConnectionsPerPartition=15<br/>
* ##设置连接池设在每个分区中的最小连接数<br/>
* minConnectionsPerPartition=10<br/>
* ##设置分区(设置 2个分区)<br/>
* partitionCount=2<br/>
* ##连接池中的连接耗尽的时候 BoneCP一次同时获取的连接数<br/>
* acquireIncrement=5<br/>
* <span style="color:red">其他自定义参数需以Driver.打头才可注入</span>
* @see com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
* @author Lipsion
*
*/
public class BoneCPDataSourceFactory implements DataSourceFactory {
private static final Probe PROBE = ProbeFactory.getProbe();
private static final String ADD_DRIVER_PROPS_PREFIX = "Driver.";
private static final int ADD_DRIVER_PROPS_PREFIX_LENGTH = ADD_DRIVER_PROPS_PREFIX
.length();
private DataSource dataSource;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void initialize(Map map) {
try {
dataSource = legacyBoneCPConfiguration(map);
if (dataSource == null) {
dataSource = newBoneCPConfiguration(map);
}
} catch (Exception e) {
throw new RuntimeException(
"Error initializing BoneCPDataSourceFactory. Cause: " + e,
e);
}
}
public DataSource getDataSource() {
return dataSource;
}
private BoneCPDataSource legacyBoneCPConfiguration(Map<String, String> map) {
BoneCPDataSource dataSource = null;
if (map.containsKey("driverClass")) {
dataSource = new BoneCPDataSource();
String driver = map.get("driverClass");
String url = map.get("jdbcUrl");
String username = map.get("username");
String password = map.get("password");
String validationQuery = map.get("connectionTestStatement");
// 最大连接数
String maxActive = map.get("maxConnectionsPerPartition");
// 最大空闲数(最小连接数)
String maxIdle = map.get("minConnectionsPerPartition");
String maxWait = map.get("idleMaxAge");
// 连接池中的连接耗尽的时候 BoneCP一次同时获取的连接数 ,默认为2
String acquireIncrement = map.get("acquireIncrement");
if (!notEmpty(url)) {
throw new RuntimeException(
"Error initializing configuration. cause: jdbcUrl is empty");
}
if (!isNumeric(maxActive)) {
throw new RuntimeException(
"Error initializing configuration. cause: maxConnectionsPerPartition is not numeric");
}
if (!isNumeric(maxIdle)) {
throw new RuntimeException(
"Error initializing configuration. cause: minConnectionsPerPartition is not numeric");
}
if (!isNumeric(maxIdle)) {
throw new RuntimeException(
"Error initializing configuration. cause: idleMaxAge is not numeric");
}
if (!isNumeric(maxWait)) {
throw new RuntimeException(
"Error initializing configuration. cause: idleMaxAge is not numeric");
}
//可选项
if (notEmpty(acquireIncrement)){
dataSource.setAcquireIncrement(Integer
.parseInt(acquireIncrement));
}
dataSource.setJdbcUrl(url);
dataSource.setDriverClass(driver);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setConnectionTestStatement(validationQuery);
dataSource.setMaxConnectionsPerPartition(Integer
.parseInt(maxActive));
dataSource.setMinConnectionsPerPartition(Integer.parseInt(maxIdle));
dataSource.setMaxConnectionAge(Long.parseLong(maxWait),
TimeUnit.SECONDS);
Iterator<String> props = map.keySet().iterator();
while (props.hasNext()) {
String propertyName = (String) props.next();
if (propertyName.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
String value = (String) map.get(propertyName);
// 映射返回对应的类型
Object convertedValue = convertValue(dataSource,
propertyName.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value);
PROBE.setObject(dataSource, propertyName.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), convertedValue);
}
}
}
return dataSource;
}
private BoneCPDataSource newBoneCPConfiguration(Map<String, String> map) {
BoneCPDataSource dataSource = null;
Iterator<String> props = map.keySet().iterator();
while (props.hasNext()) {
String propertyName = (String) props.next();
if (propertyName.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
String value = (String) map.get(propertyName);
PROBE.setObject(dataSource,
propertyName.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH),
value);
} else if (PROBE.hasWritableProperty(dataSource, propertyName)) {
String value = (String) map.get(propertyName);
Object convertedValue = convertValue(dataSource, propertyName,
value);
PROBE.setObject(dataSource, propertyName, convertedValue);
}
}
return dataSource;
}
private boolean notEmpty(String s) {
return s != null && s.length() > 0;
}
private boolean isNumeric(String str) {
//如果为空直接返回验证不通过
if(null==str||"".equals(str)){
return false;
}
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(str);
return m.matches();
}
@SuppressWarnings("rawtypes")
private Object convertValue(Object object, String propertyName, String value) {
Object convertedValue = value;
Class targetType = PROBE.getPropertyTypeForSetter(object, propertyName);
if (targetType == Integer.class || targetType == int.class) {
convertedValue = Integer.valueOf(value);
} else if (targetType == Long.class || targetType == long.class) {
convertedValue = Long.valueOf(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
convertedValue = Boolean.valueOf(value);
}
return convertedValue;
}
}