前市面上可用的数据源有 3 个 :c3p0, dbcp, bonecp 。对应一个数据源生产使用,主要注意以下几个点:
1. 性能
2. 自动重连
3. 安全,目前只有jboss app server内置的数据源支持数据库密码的加密。
以下为 c3p0 的配置
1 、 pool size config ,这个主要看你的应用以及数据库配置,来决定 min,max pool size;另外你也可以做一下性能测试,以寻找一个合适的数值
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="initialPoolSize"><value>10</value></property> <property name="minPoolSize"><value>5</value></property> <property name="maxPoolSize"><value>30</value></property> <property ame="acquireIncrement"><value>5</value></property> </bean>
参考: http://www.mchange.com/projects/c3p0/index.html#basic_pool_configuration
2. statement pool size (针对 PrepareStatement )
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="maxStatements"><value>30</value></property> <property name="maxStatementsPerConnection"><value>5</value></property> </bean>
参考: http://www.mchange.com/projects/c3p0/index.html#configuring_statement_pooling
3 、自动重连
如果在获取连接时抛出异常, c3p0 会捕获此异常,进行重试。
此功能一般用于生产环境数据库维护时,切换主、备机引起的问题。
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="acquireRetryAttempts"><value>30</value></property> <property name="acquireRetryDelay"><value>1000</value></property> <property name="breakAfterAcquireFailur "><value>false</value></property> </bean>
参考:http://www.mchange.com/projects/c3p0/index.html#configuring_recovery
4、安全性
在生产环境,访问数据库的密码都是经过加密的,你即使看到,也不能拿来用,那如何把这个加密的密码给c3p0用呢?
比如,实际数据的密码是pwd, 但配置文件里指定的却是b7220dfdfdf8;这就需要我们在生产环境模式下,去解码,把b7220dfdfdf8还原为pwd。
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- c3p0数据源的一个专有属性,只可以存放密码和用户名,详情可以研究它的源码,开源的吗 --> <property name="properties"> <bean class="com.common.DatasourcePropertiesFactory" factory-method="getProperties"> <constructor-arg type=" java.lang.String"> <value>${jdbc_password_encrypted}</value> </constructor-arg> <!-- 生产环境模式 ,才特殊处理加密密码--> <constructor-arg type="java.lang.String"> <value>${production}</value> </constructor-arg> </bean> </property> </bean>
DatasourcePropertiesFactory的源码:
public class DatasourcePropertiesFactory { private static final String PRODUCTION_MODE = "true"; private static final String PROP_PASSWORD = "password"; private static final String DEFAULT_SECURE_KEY = "secure key"; public static Properties getProperties(String pwd, String production) throws Exception { Properties p = new Properties(); // production mode if (PRODUCTION_MODE.equalsIgnoreCase(StringUtil.trim(production))) { try { p.setProperty(PROP_PASSWORD, decode(pwd)); } catch (Exception e) { throw e; } } return p; } //以下两个方法参考于jboss的实现 private static String encode(String secret) throws NamingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException { byte[] kbytes = DEFAULT_SECURE_KEY.getBytes(); SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish"); Cipher cipher; cipher = Cipher.getInstance("Blowfish"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encoding = cipher.doFinal(secret.getBytes()); BigInteger n = new BigInteger(encoding); return n.toString(16); } private static String decode(String secret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { byte[] kbytes = DEFAULT_SECURE_KEY.getBytes(); SecretKeySpec key = new SecretKeySpec(kbytes, "Blowfish"); BigInteger n = new BigInteger(secret, 16); byte[] encoding = n.toByteArray(); Cipher cipher = Cipher.getInstance("Blowfish"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decode = cipher.doFinal(encoding); return new String(decode); } public static void main(String[] args) throws NamingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException { String secret = "ca"; System.out.println(encode(secret)); } }
---以下无内容。