How to establish a connection pool in JDBC?

Can anybody provide examples or links on how to establish a JDBC connection pool?

From searching google I see many different ways of doing this and it is rather confusing.

Ultimately I need the code to return a java.sql.Connection object, but I am having trouble getting started..any suggestions welcome.

Update: Doesn't javax.sql or java.sql have pooled connection implementations? Why wouldn't it be best to use these?

201457 次浏览

不要重新发明轮子。

尝试一下现成的第三方组件:

  • Apache DBCP - This one is 内部使用的 Tomcat,以及 你真诚的。
  • c3p0

Apache DBCP comes with different example on how to setup a pooling 数据源. Here is one 样本 that can help you get started.

ApacheCommons 有一个用于此目的的库: 二溴丙烷。除非您对您的池有奇怪的需求,否则我会使用库,因为它肯定比您希望的更复杂、更微妙。

我建议使用 Commons-dbcp库。有大量的 例子列出了如何使用它,这里是链接到移动 很简单。用法很简单:

 BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver")
ds.setUsername("scott");
ds.setPassword("tiger");
ds.setUrl(connectURI);
...
Connection conn = ds.getConnection();

您只需要创建一次数据源,因此,如果您不知道如何创建数据源,请确保阅读了文档。如果您不知道如何正确地编写 JDBC 语句以避免资源泄漏,那么您可能还想阅读这个 维基百科页面。

通常,如果您需要一个连接池,那么您正在编写一个运行在某个托管环境中的应用程序,也就是说,您正在应用程序服务器中运行。如果是这种情况,在尝试任何其他选项之前,一定要使用 < strong > 检查应用程序服务器提供的连接池设施

开箱即用的解决方案将最好地与其他应用服务器设施集成在一起。但是,如果您不是在应用程序服务器内运行,我建议使用 Apache Commons DBCP Component。它被广泛使用,并提供了大多数应用程序所需的所有基本池功能。

正如其他人所回答的那样,你可能会对 Apache DbcpC3p0感到满意。它们都很受欢迎,而且工作得很好。

关于你的怀疑

Javax.sql 或 java.sql 没有 池连接实现? 为什么 用这个不是更好吗?

它们不提供实现,而是提供接口和一些支持类,只与实现第三方库(池或驱动程序)的程序员有关。通常你都不会看的。代码应该以透明的方式处理来自池的连接,就像它们是“普通”连接一样。

In the app server we use where I work (Oracle Application Server 10g, as I recall), pooling is handled by the app server. We retrieve a javax.sql.DataSource using a JNDI lookup with a javax.sql.InitialContext.

it's done something like this

try {
context = new InitialContext();
jdbcURL = (DataSource) context.lookup("jdbc/CachedDS");
System.out.println("Obtained Cached Data Source ");
}
catch(NamingException e)
{
System.err.println("Error looking up Data Source from Factory: "+e.getMessage());
}

(我们没有编写这个代码,它是从 这份文件复制过来的。)

如果您需要一个独立的连接池,我倾向于使用 C3P0而不是 二溴丙烷(我在此 上一个答案中已经提到过) ,我只是在重负载下使用 DBCP 时遇到了太多问题。使用 C3P0非常简单。来自 文件:

ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
cpds.setUser("swaldman");
cpds.setPassword("test-password");


// the settings below are optional -- c3p0 can work with defaults
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);


// The DataSource cpds is now a fully configured and usable pooled DataSource

但是如果您在应用服务器内部运行,我建议使用它提供的内置连接池。在这种情况下,您需要配置它(参考应用服务器的文档)并通过 JNDI 检索 DataSource:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/myDS");

Vibur DBCP 是另一个用于此目的的库。有几个例子展示了如何配置它,以便与 Hibernate、 Spring + Hibernate 一起使用,或者以编程方式使用,可以在它的网站上找到: < a href = “ http://www.vibur.org/”rel = “ nofollow norefrer”> http://www.vibur.org/

另外,请参阅免责声明 给你

HikariCP

它很现代,很快,很简单,我每个新项目都用它。 比起 C3P0我更喜欢它,不太了解其他游泳池。

如果您正在寻找可嵌入的解决方案,并且不太关心性能(尽管我还没有在这方面测试过) ,那么 MiniConnectionPoolManager 是一个单 Java 文件实现。

它是多许可的 英超LGPLMPL

它的文档还提供了值得检查的备选方案(在 DBCP 和 C3P0之上) :

泳池

  • 池机制是预先创建对象的方法。
  • It improves the application performance [By re using same object's to perform any action on Object-Data] & memory [allocating and de-allocating many objects creates a significant memory management overhead].
  • Object clean-up is not required as we are using same Object, reducing the Garbage collection load.

”池[ Object池,String常量池,Thread池,连接池]

字符串常量池

  • 字符串文字池只维护每个不同字符串值的一个副本。这个副本必须是不可变的。
  • 当调用实习生方法时,它使用 equals 方法检查池中具有相同内容的对象可用性。 ”如果 String-copy 在 Pool 中可用,则返回引用。 ”否则,将 String 对象添加到池中并返回引用。

示例: 从池验证 唯一对象的字符串。

public class StringPoolTest {
public static void main(String[] args) { // Integer.valueOf(), String.equals()
String eol = System.getProperty("line.separator"); //java7 System.lineSeparator();


String s1 = "Yash".intern();
System.out.format("Val:%s Hash:%s SYS:%s "+eol, s1, s1.hashCode(), System.identityHashCode(s1));
String s2 = "Yas"+"h".intern();
System.out.format("Val:%s Hash:%s SYS:%s "+eol, s2, s2.hashCode(), System.identityHashCode(s2));
String s3 = "Yas".intern()+"h".intern();
System.out.format("Val:%s Hash:%s SYS:%s "+eol, s3, s3.hashCode(), System.identityHashCode(s3));
String s4 = "Yas"+"h";
System.out.format("Val:%s Hash:%s SYS:%s "+eol, s4, s4.hashCode(), System.identityHashCode(s4));
}
}

使用类型4 司机 < em > 使用第三方库的连接池[ DBCP2c3p0Tomcat JDBC]

Type 4 - The Thin driver converts JDBC calls directly into the vendor-specific database protocol Ex[Oracle - Thick, MySQL - Quora]. 维基百科

在 Connection pool 机制中,当类被加载时,它将获得 physical JDBC connection对象并向用户提供一个包装的物理连接对象。PoolableConnection是实际连接的包装器。

  • getConnection()从连接 物体池中选择一个自由包装连接并返回它。
  • close()不关闭它,而是将包装连接返回到池。

示例: 在 Java7中使用 ~ DBCP2连接池[ try-with-resources]

public class ConnectionPool {
static final BasicDataSource ds_dbcp2 = new BasicDataSource();
static final ComboPooledDataSource ds_c3p0 = new ComboPooledDataSource();
static final DataSource ds_JDBC = new DataSource();


static Properties prop = new Properties();
static {
try {
prop.load(ConnectionPool.class.getClassLoader().getResourceAsStream("connectionpool.properties"));


ds_dbcp2.setDriverClassName( prop.getProperty("DriverClass") );
ds_dbcp2.setUrl( prop.getProperty("URL") );
ds_dbcp2.setUsername( prop.getProperty("UserName") );
ds_dbcp2.setPassword( prop.getProperty("Password") );
ds_dbcp2.setInitialSize( 5 );


ds_c3p0.setDriverClass( prop.getProperty("DriverClass") );
ds_c3p0.setJdbcUrl( prop.getProperty("URL") );
ds_c3p0.setUser( prop.getProperty("UserName") );
ds_c3p0.setPassword( prop.getProperty("Password") );
ds_c3p0.setMinPoolSize(5);
ds_c3p0.setAcquireIncrement(5);
ds_c3p0.setMaxPoolSize(20);


PoolProperties pool = new PoolProperties();
pool.setUrl( prop.getProperty("URL") );
pool.setDriverClassName( prop.getProperty("DriverClass") );
pool.setUsername( prop.getProperty("UserName") );
pool.setPassword( prop.getProperty("Password") );
pool.setValidationQuery("SELECT 1");// SELECT 1(mysql) select 1 from dual(oracle)


pool.setInitialSize(5);
pool.setMaxActive(3);
ds_JDBC.setPoolProperties( pool );
} catch (IOException e) {   e.printStackTrace();
} catch (PropertyVetoException e) { e.printStackTrace(); }
}


public static Connection getDBCP2Connection() throws SQLException {
return ds_dbcp2.getConnection();
}


public static Connection getc3p0Connection() throws SQLException {
return ds_c3p0.getConnection();
}


public static Connection getJDBCConnection() throws SQLException {
return ds_JDBC.getConnection();
}
}
public static boolean exists(String UserName, String Password ) throws SQLException {
boolean exist = false;
String SQL_EXIST = "SELECT * FROM users WHERE username=? AND password=?";
try ( Connection connection = ConnectionPool.getDBCP2Connection();
PreparedStatement pstmt = connection.prepareStatement(SQL_EXIST); ) {
pstmt.setString(1, UserName );
pstmt.setString(2, Password );


try (ResultSet resultSet = pstmt.executeQuery()) {
exist = resultSet.next(); // Note that you should not return a ResultSet here.
}
}
System.out.println("User : "+exist);
return exist;
}

jdbc:<DB>:<drivertype>:<HOST>:<TCP/IP PORT>:<dataBaseName> jdbc:oracle:thin:@localhost:1521:myDBName jdbc:mysql://localhost:3306/myDBName

Connectionpool 属性

URL         : jdbc:mysql://localhost:3306/myDBName
DriverClass : com.mysql.jdbc.Driver
UserName    : root
Password    :

Web Application: < em > 为了避免在关闭所有连接时出现连接问题[ MySQL“ wait _ timeout”default 8 hours ] ,以便重新打开与底层数据库的连接。

您可以通过设置 testOnBorrow = true 和 validationQuery = “ SELECT 1”来测试每个连接,并且不要使用 MySQL 服务器的 autoReconconnect,因为它已经被废弃了。问题

===== ===== context.xml ===== =====
<?xml version="1.0" encoding="UTF-8"?>
<!-- The contents of this file will be loaded for a web application -->
<Context>
<Resource name="jdbc/MyAppDB" auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource"


initialSize="5" minIdle="5" maxActive="15" maxIdle="10"


testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"


testOnBorrow="true"
validationQuery="SELECT 1"
validationInterval="30000"




driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/myDBName"
username="yash" password="777"
/>
</Context>


===== ===== web.xml ===== =====
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/MyAppDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
===== ===== DBOperations ===== =====
servlet «   init() {}
Normal call used by sevlet  « static {}


static DataSource ds;
static {
try {
Context ctx=new InitialContext();
Context envContext = (Context)ctx.lookup("java:comp/env");
ds  =   (DataSource) envContext.lookup("jdbc/MyAppDB");
} catch (NamingException e) {   e.printStackTrace();    }
}

看看这些:

你应该考虑使用 UCP。 Universal Connection Pool (UCP) is a Java connection pool. It is a features rich connection pool and tightly integrated with Oracle's Real Application Clusters (RAC), ADG, DG databases.

有关 UCP 的详细信息,请参阅此 呼叫

2017年下半年,Proxool,BoneCP,C3P0,DBCP 在这个时候大部分都不存在了。HikariCP (创建于2012年)看起来很有前途,它打破了我所知道的一切。 Http://www.baeldung.com/hikaricp

Proxool 有很多问题:
- 在重负荷情况下,连接次数可能超过最大值,并且不能低于最大值返回
- 可以设法不返回到最小连接,即使连接过期
- 可以锁定整个池(和所有的服务器/客户端线程) ,如果它有问题连接到数据库在 HouseKeeper 线程(不使用。SetQueryTimeout)
- HouseKeeper 线程在其进程有连接池锁定时,请求 Prototyper 线程重新创建连接(清除) ,这可能导致竞态条件/锁定。在这些方法调用中,最后一个参数在循环期间应该总是席卷: false,在它下面只有席卷: true。
- HouseKeeper 只需要在最后清理一个 PrototypeController,并且还有更多(上面提到的)
- HouseKeeper 线程在查看哪些连接可能已经过期之前检查连接的测试情况[一些测试过期连接的风险,这些过期连接可能通过防火墙中的其他超时到 DB 而中断/终止,等等]
- 项目有未完成的代码(已定义但未执行的属性)
- 如果没有定义,默认的最大连接寿命是4小时(过长)
- 每个泳池每5秒运行一次管家线程(过量)

您可以修改代码并进行这些改进。但是由于它创建于2003年,并在2008年更新,它缺乏近10年的 Java 改进,解决方案如 hikaricp 利用。