Як встановити пул з'єднань у JDBC?


111

Чи може хтось надати приклади чи посилання щодо того, як встановити пул з'єднань JDBC?

З пошуку Google я бачу багато різних способів зробити це, і це досить заплутано.

Зрештою, мені потрібен код, щоб повернути java.sql.Connectionоб'єкт, але у мене виникають проблеми з початком роботи.

Оновлення: Чи немає javax.sqlчи java.sqlє об'єднані реалізації з'єднання? Чому б не було найкраще їх використовувати?


8
Ні, на складі JDBC не забезпечує об'єднання з'єднань. Для цього вам потрібна окрема бібліотека. Більшість серверів додатків і контейнерів сервлетів включають в себе пули підключення. Крім того, реалізації JPA зазвичай також забезпечують реалізацію.
Вілл Хартунг

3
Оновлення сучасних користувачів Java. JDBC 3.0+ (який я вважаю, що використовується в Java 6?) Має реалізацію для об'єднаних з'єднань БД. У Java 7 використовується JDBC 4, а Java 8 JDBC 4.1.
BRasmussen

1
Щодо API JDBC 3.0 для об'єднання з'єднань: progress.com/tutorials/jdbc/jdbc-jdbc-connection-pooling
Arto Bendiken

Відповіді:


102

Якщо вам потрібен пул з'єднань автономного, моє перевага віддається C3P0 над ДБХПОМ (що я вже згадував в цьому попередній відповіді ), я просто мав надто багато проблем з ГСБДОМ при великому навантаженні. Використання 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 

Але якщо ви працюєте на сервері додатків, я б рекомендував використовувати вбудований пул з'єднань, який він надає. У цьому випадку вам потрібно буде налаштувати його (див. Документацію вашого сервера додатків) та отримати DataSource через JNDI:

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

1
Дітто, що. Я спостерігав тупик DBCP під навантаженням уже багато років. Версія за версією.
Василь

так, але C3P0 також, у мене був найкращий досвід роботи з BoneCP
Nicolas Mommaerts

1
Схоже, BoneCP застаріло на користь HikariCP . HikariCP також згадується у відповіді нижче .
kaartic

19

Зазвичай, якщо вам потрібен пул з'єднань, ви пишете програму, яка працює в якомусь керованому середовищі, тобто ви працюєте на сервері додатків. У такому випадку не забудьте перевірити, які засоби об’єднання об’єднань надає ваш сервер додатків перш ніж спробувати будь-які інші параметри.

Вирішене рішення буде найкращим чином інтегровано з рештою серверів додатків. Якщо ви не працюєте на сервері додатків, я рекомендую компонент DBCP Apache Commons . Він широко використовується і забезпечує всі основні функціональні можливості об’єднання для більшості програм.


18

HikariCP

Це сучасне, це швидко, це просто. Я використовую його для кожного нового проекту. Я вважаю за краще це над C3P0, не знаю інших басейнів занадто добре.


18

Не винаходити колесо.

Спробуйте один із доступних сторонніх компонентів:

  • Apache DBCP - Цей використовується внутрішньо Tomcat, і ваш справді.
  • c3p0

Apache DBCP поставляється з іншим прикладом того, як налаштувати об'єднання javax.sql.DataSource . Ось один зразок, який може допомогти вам розпочати роботу.


1
Це називається C3P0. Це, до речі, більш ефективно, ніж DBCP у багатопотокових середовищах, оскільки DBCP блокує доступ до однієї нитки.
BalusC

@BalusC. Дякую за виправлення, моє disclecsiaстало краще від мене. Ви можете бачити, що посилання правильне. :)
Олександр Погребняк

1
@Mudassir. Я б рекомендував переглянути заміну DBCP, що випадає на додаток Tomcat з Spring -> static.springsource.com/projects/tc-server/2.0/admin/htmlsingle/… . Для його використання вам не потрібен весь сервер Tomcat, лише одна банка tomcat-jdbc. Ви можете отримати його з Maven Central -> org.apache.tomcat:tomcat-jdbc:jar:7.0.22-> search.maven.org/…
Олександр Погребняк

@AlexanderPogrebnyak: Дякую, Олександре, приємно з тебе. Я планую використовувати CP у веб-сервісі Axis. Подумає над вашою пропозицією. - 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, щоб не витікати ресурси, ви також можете прочитати цю сторінку Вікіпедії .


8
Це насправді створює пул зв’язків?
llm

@llm Звичайно! Визначення javax.sql.DataSourceінтерфейсу містить реалізацію " Об'єднання об'єднань" (крім того, я думаю, ви вже знаєте, що таке інтерфейс JDBC)
Едді

7

На сервері додатків ми використовуємо те, де я працюю (як я пам’ятаю, Oracle Server Server 10g), об'єднанням обробляється сервер додатків. Ми отримуємо javax.sql.DataSourceвикористовуючи пошук JNDI з ajavax.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[Повторне використання одного і того ж об'єкта для виконання будь-яких дій над Data-Object] та memory[виділення та розподілення багатьох об'єктів створює значні накладні витрати на управління пам'яттю].
  • Очищення об'єктів не потрібно, оскільки ми використовуємо той самий Об'єкт, зменшуючи завантаження сміття.

« ObjectПул [ басейн, Stringпостійний басейн, Threadбасейн, басейн підключення]

Струнний постійний басейн

  • Лінійний пул рядків підтримує лише одну копію кожного окремого значення рядка. який повинен бути незмінним.
  • Коли викликається метод інтерну, він перевіряє наявність об'єкта з тим самим вмістом у пулі, використовуючи метод рівняння. «Якщо String-copy є в пулі, то повертає посилання. «В іншому випадку об'єкт 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 драйвера з використанням 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]. вікі

У механізмі пулу підключення при завантаженні класу він отримує physical JDBC connectionоб'єкти та надає обгорнутий фізичний об'єкт з'єднання з користувачем. PoolableConnectionє обгорткою навколо фактичного з'єднання.

  • getConnection()виберіть одну з вільних форм загорнутого з'єднання, об'єкт з'єднання і поверне його.
  • close() замість закриття повертає завернуте з'єднання назад у пул.

Приклад: Використання ~ DBCP2 Connection Pool з 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    :

Веб- додаток: Щоб уникнути проблеми з підключенням, коли всі з'єднання закриті [MySQL "wait_timeout" за замовчуванням 8 годин], щоб відновити з'єднання з базовою базою даних.

Ви можете зробити це для тестування кожного з'єднання, встановивши testOnBorrow = true та validationQuery = "SELECT 1" і не використовуйте autoReconnect для MySQL-сервера, оскільки він застарілий. проблема

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

Дивіться також:


У прикладі пулу String Constant я розумію, коли ви писали "Якщо String-копія є доступною. Але у public class StringPoolTestсправедливих є 2 недійсні методи, тому вони нічого не повертають. Чи справді цей код проходить процес управління пулом рядків? Він навіть не використовує жодних аргументів.
jeffery_the_wind

@jeffery_the_wind: - просто знати концепцію пулу, для перевірки пулу рядків я просто використовував hashCode, identHashCode методи . змінив код ...
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, маючи блокування з'єднання пулів для свого процесу, просить прототип протоколу відновити з'єднання (розгортання), що може призвести до стану гонки / блокування. У цих викликах методу останній параметр повинен бути завжди промітним: false під час циклу, тільки sweep: true під ним.
- HouseKeeper потребує лише однієї підмітки PrototypeController в кінці і має більше [згадане вище]
- перевірка потоку HouseKeeper для тестування з'єднань перед тим, як побачити, які з’єднання можуть закінчитися (деякий ризик тестування закінчився, який може бути розірваний / припинений через інші таймаути до БД в брандмауері тощо)
- Проект має незакінчений код (властивості, які визначені але не застосовується)
- Максимальний термін з'єднання за замовчуванням, якщо не визначено, становить 4 години (надмірний)
- Нитка HouseKeeper працює кожні п’ять секунд на пул (надмірна)

Ви можете змінювати код і вносити ці вдосконалення. Але оскільки він був створений у 2003 році та оновлений у 2008 році, йому не вистачає майже 10 років вдосконалення Java, які використовують такі рішення, як hikaricp.


4

Як відповіли інші, ви, ймовірно, будете задоволені Apache Dbcp або c3p0 . Обидва популярні, і працюють чудово.

Щодо вашого сумніву

Чи не має javax.sql або java.sql об'єднані реалізації з'єднання? Чому б не було найкраще їх використовувати?

Вони не пропонують реалізацій, а швидше інтерфейси та деякі класи підтримки, лише заважають програмістам, що реалізують сторонні бібліотеки (пули чи драйвери). Зазвичай ти навіть на це не дивишся. Ваш код повинен мати прозорий зв'язок із з'єднаннями з вашого пулу так само, як вони були "звичайними".


4

Vibur DBCP - це ще одна бібліотека для цієї мети. На його веб-сайті можна знайти кілька прикладів, що показують, як налаштувати його для використання в режимі Hibernate, Spring + Hibernate або програмно: http://www.vibur.org/

Також дивіться застереження тут .


3

Для цього Apache Commons має бібліотеку: DBCP . Якщо у вас немає дивних вимог навколо ваших басейнів, я б користувався бібліотекою, оскільки це, мабуть, складніше і тонкіше, ніж ви би сподівалися.


1

Вам слід розглянути можливість використання UCP. Універсальний пул підключення (UCP) - пул з'єднань Java. Це багатий зв’язок з можливостями і тісно інтегрований з реальними прикладними кластерами Oracle (RAC), базами даних ADG, DG.

На цій сторінці див. Докладнішу інформацію про UCP.


0

MiniConnectionPoolManager це реалізація файлів у форматі java-файл, якщо ви шукаєте вбудоване рішення і не надто переймаєтесь характеристиками (хоча я ще не перевіряв це з цього приводу).

Це багатоліцензійні EPL , LGPL та MPL .

Документація також дає альтернативи, які варто перевірити (поверх DBCP та C3P0):

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.