如何在JDBC中建立连接池?


111

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

通过搜索谷歌,我看到了许多不同的方法来做到这一点,这很令人困惑。

最终,我需要返回一个java.sql.Connection对象的代码,但是在上手时遇到了麻烦。欢迎任何建议。

更新: 没有javax.sqljava.sql没有池连接实现?为什么不最好使用这些?


8
不,现有的JDBC不提供连接池。您需要一个单独的库。大多数应用程序服务器和servlet容器都随附有连接池。同样,JPA实现通常也提供实现。
Will Hartung 2010年

3
适用于现代Java用户的更新。JDBC 3.0+(我相信可以在Java 6中使用它吗?)具有用于池化DB连接的实现。Java 7使用JDBC 4和Java 8 JDBC 4.1。
BRasmussen

Answers:


102

如果您需要一个独立的连接池,那么我首选的是C3P0而不是DBCP(在上一个答复中已经提到),在高负载下,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 ds = (DataSource) new InitialContext().lookup("jdbc/myDS");

1
同上 多年来,我一直在观察DBCP在负载下的死锁。一个又一个的版本。
Vasiliy

是的,但还有C3P0,我在BoneCP方面拥有最好的经验
Nicolas Mommaerts 2015年

1
像BoneCP看起来已经过时赞成HikariCP。在下面的答案中也提到了HikariCP 。
kaartic

19

通常,如果需要连接池,则需要编写在某些托管环境中运行的应用程序,也就是说,您正在应用程序服务器内部运行。如果是这种情况,确保在尝试任何其他选项之前,请检查您的应用程序服务器提供了哪些连接池功能。

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


18

光ikaCP

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


18

不要重新发明轮子。

尝试使用现成的第三方组件之一:

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


1
它称为C3P0。在多线程环境中,它比DBCP更具性能,因为DBCP锁定对单个线程的访问。
BalusC 2010年

@BalusC。感谢您的纠正,我disclecsia变得更好。您可以看到该链接是正确的。:)
Alexander Pogrebnyak,2010年

1
@Mudassir。我建议您从Spring- > static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/…中寻找对Tomcat贡献的DBCP的替代品。您不需要整个Tomcat服务器即可使用它,只需一个jar即可tomcat-jdbc。您可以从Maven Central-> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…中
Alexander Pogrebnyak

@AlexanderPogrebnyak:谢谢亚历山大,你真好。我计划在Axis Web服务中使用CP。会考虑您的建议。– Mudassir 7分钟前
Mudassir

17

我建议使用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语句,从而不会泄漏资源,则可能还需要阅读此Wikipedia页面。


8
这实际上会创建一个连接池吗?
llm 2010年

@llm当然!该javax.sql.DataSource接口的定义包含“连接池“的实施(再说,我想你已经知道一个JDBC接口是什么)。
艾迪

7

在应用服务器中,我们使用我工作的地方(我记得,Oracle应用服务器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());
}

(我们没有编写此代码,而是从本文档中复制的。)


5

泳池

  • 池机制是预先创建对象的方法。加载类时。
  • 它改进了应用程序performance[通过重新使用同一对象对对象数据执行任何操作]和memory[分配和取消分配许多对象会产生大量的内存管理开销]。
  • 由于我们正在使用相同的对象,因此不需要清理对象,从而减少了垃圾回收的负担。

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

字符串常量池

  • 字符串文字池仅维护每个不同字符串值的一个副本。这必须是不变的。
  • 调用intern方法时,它将使用equals方法检查池中具有相同内容的对象可用性。«如果池中有字符串复制,则返回引用。«否则,将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连接池的驱动程序 使用第三方库[ DBCP2c3p0Tomcat JDBC]

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

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

  • getConnection()从连接对象池中选择一个自由包装连接之一并返回它。
  • 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”来测试每个连接,并且不要对MySQL服务器使用autoReconnect,因为它已过时。问题

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

另请参阅以下内容:


在“字符串常量”池示例中,当您写到“如果字符串复制在池中可用[.equals()]然后返回引用。«否则,将String对象添加到池中并返回引用。” 但是在public class StringPoolTestJust中有2个void方法,因此它们不返回任何内容。该代码实际上是否经过了字符串池的管理过程?它甚至似乎都不使用任何参数。
jeffery_the_wind

@jeffery_the_wind:-只是知道池的概念,对于字符串池验证,我只使用了hashCode,identityHashCode 方法。修改了代码...
Yash

对不起,s1没有定义吗?
jeffery_the_wind

好的,只是想确保我能看到所有内容。我会努力的。我需要更接近您的ConnectionPool班级的东西。非常感谢。
jeffery_the_wind

5

在2017年底,Proxool,BoneCP,C3P0,DBCP目前大部分时间都已停用。HikariCP(创建于2012年)似乎很有前途,让我知道的其他一切都没了。 http://www.baeldung.com/hikaricp

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

您可以修改代码并进行这些改进。但是,由于它于2003年创建,并于2008年进行了更新,因此它缺少将近10年的Java改进,而hikaricp等解决方案都无法利用它。


4

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

关于你的疑问

javax.sql或java.sql是否没有池化连接实现?为什么不最好使用这些?

它们不提供实现,而是提供接口和一些支持类,仅对实现第三方库(池或驱动程序)的程序员有所帮助。通常情况下,您甚至都不看。您的代码应以透明方式处理池中的连接,就像它们是“普通”连接一样。


4

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

另外,请参阅此处的免责声明。


3

Apache Commons为此目的提供了一个库:DBCP。除非您对池有特殊要求,否则我会使用一个库,因为它一定比您希望的更棘手,更精巧。


1

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.