Jak ustanowić pulę połączeń w JDBC?

111

Czy ktoś może podać przykłady lub linki dotyczące sposobu ustanawiania puli połączeń JDBC?

Z wyszukiwania w Google widzę wiele różnych sposobów na zrobienie tego i jest to raczej mylące.

Ostatecznie potrzebuję kodu, aby zwrócić java.sql.Connectionobiekt, ale mam problem z rozpoczęciem ... mile widziane sugestie.

Aktualizacja: nie ma javax.sqllub nie java.sqlma implementacji połączeń w puli? Dlaczego nie byłoby najlepiej ich używać?

llm
źródło
8
Nie, standardowy JDBC nie zapewnia buforowania połączeń. Potrzebujesz do tego osobnej biblioteki. Większość serwerów aplikacji i kontenerów serwletów zawiera pule połączeń. Ponadto implementacje JPA zazwyczaj również zapewniają implementacje.
Will Hartung
3
Aktualizacja dla współczesnych użytkowników języka Java. JDBC 3.0+ (który, jak sądzę, jest używany w Javie 6?) Ma implementację dla połączeń z bazą danych w puli. Java 7 korzysta z JDBC 4 i Java 8 JDBC 4.1.
BRasmussen,
1
Odnośnie interfejsu API JDBC 3.0 do buforowania połączeń: progress.com/tutorials/jdbc/jdbc-jdbc-connection-pooling
Arto Bendiken

Odpowiedzi:

102

Jeśli potrzebujesz puli połączeń autonomicznego, moja preferencja idzie C3P0 nad DBCP (to już wspomniano w tej poprzedniej odpowiedzi ), po prostu miał zbyt wiele problemów z DBCP pod dużym obciążeniem. Korzystanie z C3P0 jest banalnie proste. Z dokumentacji :

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 

Ale jeśli pracujesz na serwerze aplikacji, zalecałbym użycie wbudowanej puli połączeń, którą zapewnia. W takim przypadku musisz go skonfigurować (zapoznaj się z dokumentacją serwera aplikacji) i pobrać źródło danych za pośrednictwem JNDI:

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/myDS");
Pascal Thivent
źródło
1
To samo. Od lat obserwuję zakleszczenie DBCP pod obciążeniem. Wersja po wersji.
Wasilij
tak, ale C3P0 też, miałem najlepsze doświadczenia z BoneCP
Nicolas Mommaerts
1
Wygląda na to, że BoneCP został wycofany na korzyść HikariCP . HikariCP jest również wymieniony w odpowiedzi poniżej .
kaartic
19

Zwykle, jeśli potrzebujesz puli połączeń, piszesz aplikację, która działa w jakimś zarządzanym środowisku, to znaczy na serwerze aplikacji. W takim przypadku przed wypróbowaniem innych opcji należy sprawdzić, jakie funkcje buforowania połączeń zapewnia serwer aplikacji .

Gotowe rozwiązanie będzie najlepiej zintegrowane z pozostałymi serwerami aplikacji. Jeśli jednak nie korzystasz z serwera aplikacji, polecam komponent Apache Commons DBCP . Jest szeroko stosowany i zapewnia wszystkie podstawowe funkcje tworzenia pul, których wymaga większość aplikacji.

Tendayi Mawushe
źródło
18

HikariCP

Jest nowoczesny, szybki, prosty. Używam go do każdego nowego projektu. Wolę to dużo od C3P0, nie znam zbyt dobrze innych pul.

tobijdc
źródło
18

Nie odkrywaj na nowo koła.

Wypróbuj jeden z łatwo dostępnych komponentów innych firm:

  • Apache DBCP - ten jest używany wewnętrznie przez Tomcat i naprawdę przez Ciebie.
  • c3p0

Apache DBCP zawiera inny przykład, jak skonfigurować buforowanie javax.sql.DataSource . Oto jedna próbka, która pomoże Ci zacząć.

Alexander Pogrebnyak
źródło
1
Nazywa się C3P0. Nawiasem mówiąc, jest bardziej wydajny niż DBCP w środowiskach wielowątkowych, ponieważ DBCP blokuje dostęp do pojedynczego wątku.
BalusC
@BalusC. Dzięki za korektę, disclecsiadostałem mnie lepiej. Widać, że link jest poprawny. :)
Alexander Pogrebnyak
1
@Mudassir. Poleciłbym przyjrzeć się zamiennikowi DBCP dodanym do Tomcata ze Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Nie potrzebujesz całego serwera Tomcat, aby go używać, wystarczy jeden słoik tomcat-jdbc. Możesz go pobrać z Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…
Alexander Pogrebnyak
@AlexanderPogrebnyak: Dziękuję Alexander, to miło z twojej strony. Planuję używać CP w serwisie internetowym Axis. Przemyślę Twoją sugestię. - Mudassir 7 minut temu
Mudassir
17

Polecam korzystanie z biblioteki commons-dbcp . Istnieje wiele przykładów, jak go używać, tutaj jest link do prostego przenoszenia . Użycie jest bardzo proste:

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

Wystarczy tylko raz utworzyć źródło danych, więc jeśli nie wiesz, jak to zrobić, przeczytaj dokumentację. Jeśli nie wiesz, jak poprawnie pisać instrukcje JDBC, aby nie wyciekać zasobów, możesz również przeczytać tę stronę Wikipedii .

Eric Hauser
źródło
8
Czy to faktycznie tworzy pulę połączeń?
llm
@llm Jasne! W javax.sql.DataSourcedefinicji interfejsu zawiera implementację „Pule połączeń„(poza tym, myślę, że już wie, co to jest interfejs JDBC).
Eddy
7

Na serwerze aplikacji, którego używamy, w miejscu, w którym pracuję (Oracle Application Server 10g, o ile pamiętam), pulowanie jest obsługiwane przez serwer aplikacji. Pobieramy plik javax.sql.DataSourceprzy użyciu wyszukiwania JNDI z rozszerzeniem javax.sql.InitialContext.

zrobiono coś takiego

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

(Nie napisaliśmy tego kodu, jest on skopiowany z tej dokumentacji ).

Władca
źródło
5

Basen

  • Mechanizm łączenia to sposób tworzenia obiektów z wyprzedzeniem. Kiedy klasa jest ładowana.
  • Ulepsza aplikację performance[przez ponowne użycie tych samych obiektów do wykonywania jakichkolwiek działań na danych obiektowych] & memory[przydzielanie i cofanie alokacji wielu obiektów powoduje znaczne obciążenie zarządzania pamięcią].
  • Oczyszczanie obiektów nie jest wymagane, ponieważ używamy tego samego obiektu, co zmniejsza obciążenie wyrzucania elementów bezużytecznych.

« ObjectPula [ pula, pula Stringstała , pula, Threadpula połączeń]

Pula stała ciągów

  • Pula literałów ciągów przechowuje tylko jedną kopię każdej odrębnej wartości ciągu. które muszą być niezmienne.
  • Gdy metoda intern jest wywoływana, sprawdza dostępność obiektu o tej samej zawartości w puli przy użyciu metody equals. «Jeśli kopia ciągu jest dostępna w puli, zwraca odwołanie. «W przeciwnym razie obiekt String jest dodawany do puli i zwraca odniesienie.

Przykład: ciąg znaków do weryfikacji unikatowego obiektu z puli.

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

Puli połączeń przy użyciu typu 4 sterownik przy użyciu bibliotek 3rd Party [ 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

W mechanizmie puli połączeń, po załadowaniu klasy pobiera ona physical JDBC connectionobiekty i udostępnia użytkownikowi opakowany obiekt fizycznego połączenia. PoolableConnectionjest opakowaniem wokół rzeczywistego połączenia.

  • getConnection()wybierz jedno z wolnych połączeń opakowanych z puli obiektów połączeń i zwróć je.
  • close() zamiast zamykania zwraca opakowane połączenie z powrotem do puli.

Przykład: użycie puli połączeń ~ DBCP2 w środowisku 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    :

Aplikacja sieciowa : Aby uniknąć problemów z połączeniem, gdy wszystkie połączenia są zamknięte [domyślny limit czasu oczekiwania MySQL 8 godzin] w celu ponownego otwarcia połączenia z bazową bazą danych.

Możesz to zrobić, aby przetestować każde połączenie, ustawiając testOnBorrow = true i validationQuery = "SELECT 1" i nie używaj funkcji autoReconnect dla serwera MySQL, ponieważ jest on przestarzały. kwestia

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

Zobacz też:

Yash
źródło
W przykładzie puli String Constant rozumiem, kiedy napisałeś „Jeśli String-copy jest dostępne [.equals ()] w puli, to zwraca odwołanie.„ W przeciwnym razie obiekt String jest dodawany do puli i zwraca odwołanie ”. Ale po public class StringPoolTestprostu ma dwie metody void, więc nic nie zwracają. Czy ten kod faktycznie przechodzi przez proces zarządzania pulą ciągów? Wydaje się nawet, że nie używa żadnych argumentów.
jeffery_the_wind,
@jeffery_the_wind: - wystarczy znać koncepcję puli, do weryfikacji puli ciągów użyłem właśnie metod hashCode, identityHashCode . zmodyfikował kod ...
Yash
Przepraszam, s1nie zdefiniowano?
jeffery_the_wind,
OK, chciałem się tylko upewnić, że widzę to wszystko. Będę nad tym pracował. Czego będę potrzebować do czegoś bliższego twojej ConnectionPoolklasie. Dzięki wielkie.
jeffery_the_wind,
5

Pod koniec 2017 roku Proxool, BoneCP, C3P0, DBCP w większości nie funkcjonują w tym czasie. HikariCP (utworzony w 2012 roku) wydaje się obiecujący, wyrywa drzwi z wszystkiego, co wiem. http://www.baeldung.com/hikaricp

Proxool ma wiele problemów:
- Przy dużym obciążeniu może przekroczyć maksymalną liczbę połączeń i nie powrócić poniżej maksymalnej
- Może nie powrócić do minimalnych połączeń nawet po wygaśnięciu połączeń
- Może zablokować całą pulę (i wszystkie wątki serwera / klienta) jeśli ma problemy z połączeniem się z bazą danych podczas wątku HouseKeeper (nie używa .setQueryTimeout)
- wątek HouseKeeper, mając blokadę puli połączeń dla swojego procesu, żąda od wątku Prototyper ponownego utworzenia połączeń (wymiatania), co może spowodować stan wyścigu / zawieszenie. W wywołaniach tych metod ostatni parametr powinien być zawsze sweep: false podczas pętli, tylko sweep: true pod nim.
- HouseKeeper potrzebuje tylko jednego przemiatania PrototypeController na końcu i ma więcej [wspomniano powyżej]
- Wątek HouseKeeper sprawdza połączenia pod kątem testowania połączeń przed sprawdzeniem, które połączenia mogą wygasnąć [pewne ryzyko testowania wygasłego połączenia, które może zostać zerwane / zakończone przez inne limity czasu do bazy danych w zaporze itp.]
- Projekt ma niedokończony kod (właściwości, które są zdefiniowane ale nie podjęto żadnych działań)
- Domyślny maksymalny czas trwania połączenia, jeśli nie został zdefiniowany, wynosi 4 godziny (nadmierny)
- Wątek HouseKeeper jest uruchamiany co pięć sekund na pulę (nadmierny)

Możesz zmodyfikować kod i wprowadzić te ulepszenia. Ale ponieważ został utworzony w 2003 r. I zaktualizowany w 2008 r., Brakuje w nim prawie 10 lat ulepszeń Java, które wykorzystują rozwiązania takie jak hikaricp.

bluejaguar
źródło
4

Jak odpowiedzieli inni, prawdopodobnie będziesz zadowolony z Apache Dbcp lub c3p0 . Obie są popularne i działają dobrze.

Jeśli chodzi o twoje wątpliwości

Czy javax.sql lub java.sql nie ma implementacji połączeń w puli? Dlaczego nie byłoby najlepiej ich używać?

Nie zapewniają implementacji, a raczej interfejsy i niektóre klasy pomocnicze, co jest widoczne tylko dla programistów, którzy implementują biblioteki innych firm (pule lub sterowniki). Zwykle nawet na to nie patrzysz. Twój kod powinien obsługiwać połączenia z Twojej puli tak, jakby były „zwykłymi” połączeniami, w przejrzysty sposób.

leonbloy
źródło
4

Vibur DBCP to kolejna biblioteka do tego celu. Kilka przykładów pokazujących, jak skonfigurować go do użytku z Hibernate, Spring + Hibernate lub programowo, można znaleźć na jego stronie internetowej: http://www.vibur.org/

Zobacz także zrzeczenie się tutaj .

Simeon Malchev
źródło
3

Apache Commons posiada bibliotekę do tego celu: DBCP . Jeśli nie masz dziwnych wymagań dotyczących swoich basenów, użyłbym biblioteki, ponieważ z pewnością będzie trudniejsza i bardziej subtelna, niż byś się spodziewał.

sblundy
źródło
1

Powinieneś rozważyć użycie UCP. Universal Connection Pool (UCP) to pula połączeń Java. Jest to bogata w funkcje pula połączeń i ściśle zintegrowana z bazami danych Oracle Real Application Clusters (RAC), ADG i DG.

Więcej informacji na temat UCP można znaleźć na tej stronie .

Nirmala
źródło