如何在JDBC中建立连接池?

时间:2010-05-14 14:55:06

标签: java jdbc connection-pooling

任何人都可以提供有关如何建立JDBC连接池的示例或链接吗?

从搜索谷歌我看到很多不同的方法,这是相当混乱。

最终我需要代码返回一个java.sql.Connection对象,但我无法入门。欢迎提出任何建议。

更新javax.sqljava.sql是否有汇集连接实施?为什么不最好使用这些?

13 个答案:

答案 0 :(得分:99)

如果你需要一个独立的连接池,我的偏好是C3P0而不是DBCP(我已在previous answer中提到过),我在DBCP下遇到了太多问题重物。使用C3P0很简单。来自documentation

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");

答案 1 :(得分:18)

通常,如果您需要连接池,那么您正在编写在某个托管环境中运行的应用程序,即您正在应用程序服务器中运行。如果是这种情况,请在尝试任何其他选项之前确保check what connection pooling facilities your application server provides

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

答案 2 :(得分:16)

不要重新发明轮子。

尝试其中一个现成的第三方组件:

Apache DBCP提供了有关如何设置池javax.sql.DataSource的不同示例。这是一个可以帮助您入门的sample

答案 3 :(得分:16)

HikariCP

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

答案 4 :(得分:15)

我建议使用commons-dbcp库。有很多examples列出了如何使用它,这里是移动simple one的链接。用法非常简单:

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

您只需要创建一次数据源,因此如果您不知道如何操作,请务必阅读文档。如果您不知道如何正确编写JDBC语句以便不泄漏资源,那么您可能还需要阅读此Wikipedia页面。

答案 5 :(得分:7)

在app服务器中,我们使用我工作的地方(我记得Oracle Application Server 10g),池由应用服务器处理。我们使用带有javax.sql.DataSource的JNDI查找检索javax.sql.InitialContext

它完成了类似的事情

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());
}

(我们没有编写此代码,而是从this documentation复制。)

答案 6 :(得分:5)

<强>池

  • 池化机制是提前创建对象的方式。当一个类加载时。
  • 它改进了应用程序 performance [通过重新使用相同的对象来对对象数据执行任何操作]&amp; memory [分配和取消分配许多对象会产生大量的内存管理开销]。
  • 不需要对象清理,因为我们使用相同的Object,减少了垃圾收集负载。

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

字符串常量池

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

示例:从池中验证Unique Object的字符串。

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方Driver 的连接池使用第三方库[DBCP2c3p0Tomcat JDBC]

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

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

  • getConnection()从连接objectpool中选择一个免费的包装连接并将其返回。
  • close()而不是关闭它会将包装连接返回到池中。

示例:将~DBCP2连接池与Java 7 [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.properties

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

Web应用程序:为了避免连接问题,所有连接都关闭[MySQL“wait_timeout”默认为8小时],以便重新打开与底层数据库的连接。

你可以通过设置testOnBorrow = true和validationQuery =“SELECT 1”来测试每个连接,并且不要使用autoReconnect for MySQL server,因为它已被弃用。 issue

===== ===== 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();    }
}

另见:

答案 7 :(得分:5)

2017年末,Proxool,BoneCP,C3P0,DBCP目前大部分都已不存在。 HikariCP(创建于2012年)看起来很有希望,打破了我所知道的任何其他事情。 http://www.baeldung.com/hikaricp

Proxool有很多问题:
- 在重负载下可能超过最大连接数而不会返回到最大值以下 - 即使在连接到期后,也可以设法不返回最小连接数 - 如果在HouseKeeper线程(不使用.setQueryTimeout)期间连接到数据库时遇到问题,可以锁定整个池(以及所有服务器/客户端线程) - HouseKeeper线程在为其进程提供连接池锁定时,请求Prototyper线程重新创建连接(扫描),这可能导致竞争条件/锁定。在这些方法调用中,最后一个参数在循环期间应始终为sweep:false,仅在其下方扫描:true - HouseKeeper最后只需要单个PrototypeController扫描,并且有更多[上面提到]
- HouseKeeper线程在查看可能已过期的连接之前检查连接测试[测试过期连接的风险可能会因防火墙中的DB等其他超时而中断/终止等] - 项目有未完成的代码(已定义但未采取行动的属性)
- 未定义的默认最大连接寿命为4小时(过量)
- HouseKeeper线程每个池每5秒运行一次(过量)

您可以修改代码并进行这些改进。但由于它是在2003年创建的,并在2008年更新,因此它缺乏近十年的Java改进,而像hikaricp这样的解决方案正在使用。

答案 8 :(得分:4)

正如其他人所回答,您可能会对Apache Dbcpc3p0感到满意。两者都很受欢迎,并且工作正常。

关于你的疑问

  

不是javax.sql或java.sql   汇集连接实现?为什么   使用这些不是最好吗?

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

答案 9 :(得分:4)

Vibur DBCP 是另一个用于此目的的库。有几个示例显示如何配置它以便与Hibernate,Spring + Hibernate一起使用或以编程方式使用,可以在其网站上找到:http://www.vibur.org/

另请参阅免责声明here

答案 10 :(得分:3)

Apache Commons有一个用于此目的的库:DBCP。除非你对你的游泳池有奇怪的要求,否则我会使用一个库,因为它必然比你希望的更复杂,更微妙。

答案 11 :(得分:1)

您应该考虑使用UCP。 Universal Connection Pool (UCP)是一个Java连接池。它是一个功能丰富的连接池,与Oracle的Real Application Clusters(RAC),ADG,DG数据库紧密集成。

有关UCP的更多详细信息,请参阅此page

答案 12 :(得分:0)

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

它是多次许可的EPLLGPLMPL

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