簡體   English   中英

如何在JDBC中建立連接池?

[英]How to establish a connection pool in JDBC?

有人可以提供有關如何建立JDBC連接池的示例或鏈接嗎?

從搜索谷歌,我看到這樣做的許多不同方式,這相當令人困惑。

最終,我需要代碼來返回java.sql.Connection對象,但是我在入門時遇到了麻煩。歡迎任何建議。

更新: javax.sqljava.sql是否沒有池化連接實現? 為什么不最好使用這些?

如果您需要一個獨立的連接池,那么我首選的是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");

不要重新發明輪子。

嘗試使用現成的第三方組件之一:

Apache DBCP附帶了有關如何設置池javax.sql.DataSource的不同示例。 這是一個可以幫助您入門的示例

通常,如果需要連接池,則需要編寫在某些托管環境中運行的應用程序,也就是說,您正在應用程序服務器內部運行。 如果是這種情況, 確保在嘗試任何其他選項之前, 請檢查您的應用程序服務器提供了哪些連接池功能。

開箱即用的解決方案將是與其他應用程序服務器設施的最佳集成。 但是,如果您不在應用程序服務器中運行,則建議使用Apache Commons DBCP Component 它被廣泛使用,並提供大多數應用程序所需的所有基本池化功能。

我建議使用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頁面。

光ikaCP

它是現代的,快速的,簡單的。 我將其用於每個新項目。 我比C3P0更喜歡它,不太了解其他池。

在應用服務器中,我們使用我工作的地方(我記得,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());
}

(我們沒有編寫此代碼,而是從本文檔中復制的。)

泳池

  • 池機制是預先創建對象的方法。 加載類時。
  • 它提高了應用程序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));
    }
}

使用Type-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()而不是關閉它,將包裝的連接返回到池中。

示例:在Java 7中使用〜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.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();    }
}

另請參閱以下內容:

在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等解決方案都無法利用它。

Vibur DBCP是用於此目的的另一個庫。 可以在其網站上找到幾個示例,其中顯示了如何配置它以使其與Hibernate,Spring + Hibernate或以編程方式一起使用的示例: http : //www.vibur.org/

另外,請參閱此處的免責聲明。

就像其他人回答的那樣,您可能會對Apache Dbcpc3p0感到滿意。 兩者都很受歡迎,而且工作正常。

關於你的疑問

javax.sql或java.sql是否沒有池化連接實現? 為什么不最好使用這些?

它們不提供實現,而是提供接口和一些支持類,僅對實現第三方庫(池或驅動程序)的程序員有所幫助。 通常情況下,您甚至都不看。 您的代碼應以透明方式處理池中的連接,就像它們是“普通”連接一樣。

Apache Commons為此目的提供了一個庫: DBCP 除非您對池有特殊要求,否則我會使用一個庫,因為它一定比您希望的更棘手,更精巧。

您應該考慮使用UCP。 通用連接池(UCP)是Java連接池。 它是一個功能豐富的連接池,並與Oracle的Real Application Clusters(RAC),ADG,DG數據庫緊密集成。

有關UCP的更多詳細信息,請參閱此頁面

MiniConnectionPoolManager是一個Java文件的實現,如果您正在尋找一個可嵌入的解決方案,而又不太在意性能(盡管我尚未對此進行測試)。

它是多許可的EPLLGPLMPL

它的文檔還提供了值得檢查的替代方法(在DBCP和C3P0之上):

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM