Làm thế nào để thiết lập một nhóm kết nối trong JDBC?


111

Ai có thể cung cấp ví dụ hoặc liên kết về cách thiết lập nhóm kết nối JDBC không?

Từ việc tìm kiếm google, tôi thấy nhiều cách khác nhau để làm điều này và nó là khá khó hiểu.

Cuối cùng thì tôi cần mã để trả về một java.sql.Connectionđối tượng, nhưng tôi đang gặp sự cố khi bắt đầu. Rất nhiều đề xuất được chào đón.

Cập nhật: Không javax.sqlhoặc java.sqlcó triển khai kết nối gộp? Tại sao nó không phải là tốt nhất để sử dụng?


8
Không, chứng khoán JDBC không cung cấp kết nối gộp. Bạn cần một thư viện riêng cho điều đó. Hầu hết các máy chủ ứng dụng và thùng chứa servlet đều có các nhóm Kết nối đi kèm với chúng. Ngoài ra, các triển khai JPA thường cung cấp các triển khai.
Will Hartung

3
Bản cập nhật cho người dùng Java hiện đại. JDBC 3.0+ (mà tôi tin rằng được sử dụng trong Java 6?) Có một triển khai cho các kết nối DB được gộp chung. Java 7 sử dụng JDBC 4 và Java 8 JDBC 4.1.
BRasmussen

1
Về 3.0 API JDBC để kết nối tổng hợp: progress.com/tutorials/jdbc/jdbc-jdbc-connection-pooling
Arto Bendiken

Câu trả lời:


102

Nếu bạn cần một nhóm kết nối độc lập, tùy chọn của tôi chuyển đến C3P0 thay vì DBCP (mà tôi đã đề cập trong câu trả lời trước đây ), tôi chỉ gặp quá nhiều vấn đề với DBCP khi tải nặng. Sử dụng C3P0 rất đơn giản. Từ tài liệu :

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 

Nhưng nếu bạn đang chạy bên trong một máy chủ ứng dụng, tôi khuyên bạn nên sử dụng nhóm kết nối tích hợp mà nó cung cấp. Trong trường hợp đó, bạn sẽ cần phải định cấu hình nó (tham khảo tài liệu của máy chủ ứng dụng của bạn) và để truy xuất Nguồn dữ liệu qua JNDI:

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

1
Ditto, đó. Tôi đã quan sát thấy DBCP bế tắc khi tải trong nhiều năm nay. Phiên bản này đến phiên bản khác.
Vasiliy

vâng nhưng C3P0 cũng vậy, tôi đã có trải nghiệm tốt nhất với BoneCP
Nicolas Mommaerts,

1
Có vẻ như BoneCP đã không được dùng nữa để thay thế cho HikariCP . HikariCP cũng được đề cập trong một câu trả lời dưới đây .
kaartic

19

Thông thường, nếu bạn cần một nhóm kết nối, bạn đang viết một ứng dụng chạy trong một số môi trường được quản lý, tức là bạn đang chạy bên trong một máy chủ ứng dụng. Nếu đúng như vậy, hãy chắc chắn kiểm tra xem máy chủ ứng dụng của bạn cung cấp các phương tiện gộp kết nối nào trước khi thử bất kỳ tùy chọn nào khác.

Giải pháp out-of-the box sẽ được tích hợp tốt nhất với phần còn lại của các cơ sở máy chủ ứng dụng. Tuy nhiên, nếu bạn không chạy bên trong một máy chủ ứng dụng, tôi khuyên bạn nên sử dụng Thành phần DBCP Apache Commons . Nó được sử dụng rộng rãi và cung cấp tất cả các chức năng gộp cơ bản mà hầu hết các ứng dụng yêu cầu.


18

HikariCP

Nó hiện đại, nó nhanh chóng, nó đơn giản. Tôi sử dụng nó cho mọi dự án mới. Tôi thích nó hơn C3P0, không biết các hồ bơi khác quá rõ.


18

Đừng phát minh lại bánh xe.

Hãy thử một trong các thành phần bên thứ 3 có sẵn:

  • Apache DBCP - Cái này được Tomcat sử dụng nội bộ và của bạn thực sự.
  • c3p0

Apache DBCP đi kèm với một ví dụ khác về cách thiết lập javax.sql.DataSource tổng hợp . Đây là một mẫu có thể giúp bạn bắt đầu.


1
Nó được gọi là C3P0. Nói cách khác, nó hoạt động hiệu quả hơn DBCP trong môi trường đa luồng vì DBCP khóa quyền truy cập vào một luồng duy nhất.
BalusC

@BalusC. Cảm ơn vì đã sửa chữa, tôi disclecsiađã làm tốt hơn cho tôi. Bạn có thể thấy rằng liên kết là chính xác. :)
Alexander Pogrebnyak

1
@Mudassir. Tôi khuyên bạn nên xem bản thay thế cho DBCP được đóng góp cho Tomcat từ Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Bạn không cần toàn bộ máy chủ Tomcat để sử dụng nó, chỉ cần một jar duy nhất tomcat-jdbc. Bạn có thể lấy nó từ Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…
Alexander Pogrebnyak,

@AlexanderPogrebnyak: Cảm ơn Alexander, bạn thật tuyệt. Tôi dự định sử dụng CP trong dịch vụ web Axis. Sẽ suy nghĩ về đề nghị của bạn. - Mudassir cách đây 7 phút
Mudassir

17

Tôi khuyên bạn nên sử dụng thư viện commons-dbcp . Có rất nhiều ví dụ được liệt kê về cách sử dụng nó, đây là liên kết đến cách di chuyển đơn giản . Cách sử dụng rất đơn giản:

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

Bạn chỉ cần tạo nguồn dữ liệu một lần, vì vậy hãy đảm bảo bạn đã đọc tài liệu nếu bạn không biết cách thực hiện điều đó. Nếu bạn không biết cách viết đúng các câu lệnh JDBC để không bị rò rỉ tài nguyên, bạn cũng có thể muốn đọc trang Wikipedia này.


8
Điều này thực sự tạo ra một nhóm kết nối?
llm

@llm Chắc chắn! Các javax.sql.DataSourceđịnh nghĩa của giao diện chứa một thực hiện "Kết nối tổng hợp“(bên cạnh đó, tôi nghĩ rằng bạn đã biết những gì một giao diện JDBC là).
Eddy

7

Trong máy chủ ứng dụng chúng tôi sử dụng nơi tôi làm việc (Máy chủ ứng dụng Oracle 10g, như tôi nhớ lại), việc gộp chung do máy chủ ứng dụng xử lý. Chúng tôi truy xuất a javax.sql.DataSourcebằng cách sử dụng tra cứu JNDI với a javax.sql.InitialContext.

nó đã hoàn thành một cái gì đó như thế này

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

(Chúng tôi không viết mã này, nó được sao chép từ tài liệu này .)


5

Hồ bơi

  • Cơ chế gộp là cách tạo các Đối tượng trước. Khi một lớp được tải.
  • Nó cải thiện ứng dụng performance[Bằng cách sử dụng lại cùng một đối tượng để thực hiện bất kỳ hành động nào trên Đối tượng-Dữ liệu] & memory[cấp phát và hủy phân bổ nhiều đối tượng tạo ra chi phí quản lý bộ nhớ đáng kể].
  • Không yêu cầu dọn dẹp đối tượng vì chúng ta đang sử dụng cùng một Đối tượng, giảm tải thu gom rác.

«Pooling [ Objectpool, StringConstant Pool, ThreadPool, Connection pool]

Nhóm chuỗi không đổi

  • Nhóm ký tự chuỗi chỉ duy trì một bản sao của mỗi giá trị chuỗi riêng biệt. mà phải là bất biến.
  • Khi phương thức intern được gọi, nó sẽ kiểm tra tính khả dụng của đối tượng có cùng nội dung trong pool bằng phương thức equals. «Nếu String-copy có sẵn trong Pool thì trả về tham chiếu. «Nếu không, đối tượng String được thêm vào nhóm và trả về tham chiếu.

Ví dụ: Chuỗi xác minh Đối tượng duy nhất từ nhóm.

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

Kết nối hồ bơi sử dụng Type-4 điều khiển sử dụng thư viện của bên thứ 3 [ DBCP2, c3p0, Tomcat JDBC]

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

Trong cơ chế nhóm kết nối, khi lớp được tải, nó nhận các physical JDBC connectionđối tượng và cung cấp một đối tượng kết nối vật lý được bao bọc cho người dùng. PoolableConnectionlà một trình bao bọc xung quanh kết nối thực tế.

  • getConnection()chọn một trong các gói kết nối miễn phí hình thức kết nối objectpool và trả về nó.
  • close() thay vì đóng nó sẽ trả kết nối được bọc trở lại nhóm.

Ví dụ: Sử dụng ~ Nhóm kết nối DBCP2 với 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    :

Ứng dụng Web : Để tránh sự cố kết nối khi tất cả kết nối bị đóng [MySQL "wait_timeout" mặc định 8 giờ] để mở lại kết nối với DB bên dưới.

Bạn có thể thực hiện việc này để Kiểm tra mọi kết nối bằng cách đặt testOnBorrow = true and validationQuery = "SELECT 1" và không sử dụng autoReconnect cho máy chủ MySQL vì nó không được dùng nữa. vấn đề

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

Xem thêm:


Trong ví dụ nhóm String Constant, tôi hiểu khi bạn viết "Nếu String-copy có sẵn [.equals ()] trong Pool thì trả về tham chiếu.« Nếu không, đối tượng String được thêm vào nhóm và trả về tham chiếu. " Nhưng trong public class StringPoolTestchỉ có 2 phương thức void nên chúng không trả về bất cứ thứ gì. Mã đó có thực sự trải qua quá trình quản lý nhóm chuỗi không? Nó thậm chí dường như không sử dụng bất kỳ đối số nào.
jeffery_the_wind

@jeffery_the_wind: - đó chỉ là để biết khái niệm nhóm, để xác minh nhóm chuỗi, tôi chỉ sử dụng các phương thức hashCode, IDHashCode . đã sửa đổi mã ...
Yash

Xin lỗi, s1không được xác định?
jeffery_the_wind

OK, chỉ muốn đảm bảo rằng tôi đã nhìn thấy tất cả. Tôi sẽ làm việc trên đó. Tôi sẽ cần thứ gì đó gần gũi hơn với ConnectionPoolLớp học của bạn . Cám ơn rất nhiều.
jeffery_the_wind

5

Vào cuối năm 2017, Proxool, BoneCP, C3P0, DBCP hầu như không còn tồn tại tại thời điểm này. HikariCP (được tạo ra vào năm 2012) có vẻ đầy hứa hẹn, thổi bay cánh cửa bất cứ điều gì khác mà tôi biết. http://www.baeldung.com/hikaricp

Proxool có một số vấn đề:
- Dưới tải nặng có thể vượt quá số lượng kết nối tối đa và không trở lại dưới mức tối đa
- Có thể quản lý để không quay lại kết nối tối thiểu ngay cả khi kết nối hết hạn
- Có thể khóa toàn bộ nhóm (và tất cả các luồng máy chủ / máy khách) nếu nó gặp sự cố khi kết nối với cơ sở dữ liệu trong chuỗi HouseKeeper (không sử dụng .setQueryTimeout)
- Chuỗi HouseKeeper, trong khi có khóa nhóm kết nối cho quy trình của nó, yêu cầu chuỗi Prototyper tạo lại các kết nối (quét), điều này có thể dẫn đến tình trạng cuộc đua / khóa. Trong các lệnh gọi phương thức này, tham số cuối cùng phải luôn luôn là scan: false trong suốt vòng lặp, chỉ có scan: true bên dưới nó.
- HouseKeeper chỉ cần quét PrototypeController duy nhất ở cuối và có nhiều hơn nữa [đã đề cập ở trên]
- Chuỗi HouseKeeper kiểm tra để kiểm tra các kết nối trước khi xem những kết nối nào có thể hết hạn [một số rủi ro khi thử nghiệm kết nối hết hạn có thể bị hỏng / chấm dứt thông qua các thời gian chờ khác tới DB trong tường lửa, v.v.]
- Dự án có mã chưa hoàn thành (các thuộc tính được xác định nhưng không được tác động)
- Tuổi thọ kết nối tối đa mặc định nếu không được xác định là 4 giờ (quá mức)
- Chuỗi HouseKeeper chạy mỗi năm giây một lần (quá mức)

Bạn có thể sửa đổi mã và thực hiện các cải tiến này. Nhưng vì nó được tạo ra vào năm 2003 và cập nhật vào năm 2008, nó đã thiếu gần 10 năm cải tiến java mà các giải pháp như hikaricp sử dụng.


4

Như những người khác đã trả lời, bạn có thể sẽ hài lòng với Apache Dbcp hoặc c3p0 . Cả hai đều phổ biến và hoạt động tốt.

Về nghi ngờ của bạn

Javax.sql hoặc java.sql không có triển khai kết nối tổng hợp? Tại sao nó không phải là tốt nhất để sử dụng?

Chúng không cung cấp các triển khai, thay vào đó là các giao diện và một số lớp hỗ trợ, chỉ cung cấp thông tin cho các lập trình viên triển khai các thư viện bên thứ ba (nhóm hoặc trình điều khiển). Bình thường bạn thậm chí không nhìn vào đó. Mã của bạn phải xử lý các kết nối từ nhóm của bạn giống như chúng là các kết nối "đơn giản", một cách minh bạch.


4

Vibur DBCP là một thư viện khác cho mục đích đó. Bạn có thể tìm thấy một số ví dụ về cách cấu hình nó để sử dụng với Hibernate, Spring + Hibernate hoặc theo chương trình trên trang web của nó: http://www.vibur.org/

Ngoài ra, hãy xem tuyên bố từ chối trách nhiệm tại đây .


3

Apache Commons có một thư viện cho mục đích đó: DBCP . Trừ khi bạn có những yêu cầu kỳ lạ xung quanh hồ bơi của mình, tôi sẽ sử dụng một thư viện vì nó chắc chắn phức tạp và tinh vi hơn bạn mong đợi.


1

Bạn nên cân nhắc sử dụng UCP. Nhóm kết nối chung (UCP) là một nhóm kết nối Java. Nó là một nhóm kết nối phong phú có nhiều tính năng và được tích hợp chặt chẽ với cơ sở dữ liệu Nhóm ứng dụng thực (RAC), ADG, DG của Oracle.

Tham khảo trang này để biết thêm chi tiết về UCP.


0

MiniConnectionPoolManager là một triển khai một tệp java, nếu bạn đang tìm kiếm một giải pháp có thể nhúng và không quá lo lắng về hiệu suất (mặc dù tôi chưa thử nghiệm về mặt đó).

Đó là EPL , LGPLMPL được cấp phép nhiều lần .

Tài liệu của nó cũng cung cấp các lựa chọn thay thế đáng để kiểm tra (trên DBCP và C3P0):

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.