Panduan Mudah untuk Penyambungan Penyambungan di Jawa

1. Gambaran keseluruhan

Penyatuan penyambungan adalah corak akses data yang terkenal, yang tujuan utamanya adalah untuk mengurangkan overhead yang terlibat dalam melakukan sambungan pangkalan data dan membaca / menulis operasi pangkalan data.

Ringkasnya, kumpulan sambungan adalah, pada tingkat paling dasar, implementasi cache sambungan pangkalan data , yang dapat dikonfigurasi untuk memenuhi persyaratan tertentu.

Dalam tutorial ini, kami akan membuat sejumlah ringkas kerangka penyatuan sambungan yang popular, dan kami akan belajar bagaimana melaksanakan dari awal kumpulan sambungan kami sendiri.

2. Mengapa Penyambungan Penyambungan?

Soalannya tentu saja retorik.

Sekiranya kita menganalisis urutan langkah-langkah yang terlibat dalam kitaran hidup sambungan pangkalan data biasa, kita akan memahami mengapa:

  1. Membuka sambungan ke pangkalan data menggunakan pemacu pangkalan data
  2. Membuka soket TCP untuk membaca / menulis data
  3. Membaca / menulis data di atas soket
  4. Menutup sambungan
  5. Menutup soket

Menjadi jelas bahawa sambungan pangkalan data adalah operasi yang cukup mahal , dan dengan demikian, harus dikurangkan menjadi minimum dalam setiap kes penggunaan yang mungkin (dalam kes tepi, hanya dihindari).

Di sinilah pelaksanaan penyatuan penyambungan.

Dengan hanya melaksanakan wadah sambungan pangkalan data, yang memungkinkan kita menggunakan kembali sejumlah sambungan yang ada, kita dapat menjimatkan kos dengan berkesan melakukan sejumlah besar perjalanan pangkalan data yang mahal, sehingga meningkatkan prestasi keseluruhan aplikasi berdasarkan pangkalan data kami.

3. Kerangka Pengumpulan Sambungan JDBC

Dari perspektif pragmatik, menerapkan kumpulan penyambungan dari bawah ke atas tidak ada gunanya, mengingat jumlah kerangka penyatuan sambungan "siap untuk perusahaan" yang tersedia di luar sana.

Dari satu didaktik, yang merupakan matlamat artikel ini, bukan.

Walaupun begitu, sebelum kita belajar bagaimana menerapkan kumpulan sambungan asas, pertama-tama kita mempamerkan beberapa kerangka penyatuan sambungan yang popular.

3.1. DBCP Apache Commons

Mari mulakan penambahan cepat ini dengan Apache Commons DBCP Component, kerangka kerja gabungan gabungan JDBC dengan ciri lengkap:

public class DBCPDataSource { private static BasicDataSource ds = new BasicDataSource(); static { ds.setUrl("jdbc:h2:mem:test"); ds.setUsername("user"); ds.setPassword("password"); ds.setMinIdle(5); ds.setMaxIdle(10); ds.setMaxOpenPreparedStatements(100); } public static Connection getConnection() throws SQLException { return ds.getConnection(); } private DBCPDataSource(){ } }

Dalam kes ini, kami telah menggunakan kelas pembungkus dengan blok statik untuk mengkonfigurasi sifat DBCP dengan mudah.

Inilah cara mendapatkan sambungan gabungan dengan kelas DBCPDataSource :

Connection con = DBCPDataSource.getConnection();

3.2. HikariCP

Melanjutkan, mari kita lihat HikariCP, kerangka penyatuan sambungan JDBC sepantas kilat yang dibuat oleh Brett Wooldridge (untuk perincian lengkap mengenai cara mengkonfigurasi dan memanfaatkan HikariCP, sila baca artikel ini):

public class HikariCPDataSource { private static HikariConfig config = new HikariConfig(); private static HikariDataSource ds; static { config.setJdbcUrl("jdbc:h2:mem:test"); config.setUsername("user"); config.setPassword("password"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); ds = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return ds.getConnection(); } private HikariCPDataSource(){} }

Begitu juga, inilah cara untuk mendapatkan sambungan gabungan dengan kelas HikariCPDataSource :

Connection con = HikariCPDataSource.getConnection();

3.3. C3PO

Terakhir dalam tinjauan ini adalah C3PO, kerangka penyambungan dan penyataan JDBC4 yang kuat yang dikembangkan oleh Steve Waldman:

public class C3poDataSource { private static ComboPooledDataSource cpds = new ComboPooledDataSource(); static { try { cpds.setDriverClass("org.h2.Driver"); cpds.setJdbcUrl("jdbc:h2:mem:test"); cpds.setUser("user"); cpds.setPassword("password"); } catch (PropertyVetoException e) { // handle the exception } } public static Connection getConnection() throws SQLException { return cpds.getConnection(); } private C3poDataSource(){} }

Seperti yang dijangkakan, mendapatkan hubungan bersama dengan kelas C3poDataSource adalah serupa dengan contoh sebelumnya:

Connection con = C3poDataSource.getConnection();

4. Pelaksanaan Yang Mudah

Untuk lebih memahami logik asas penyatuan sambungan, mari buat pelaksanaan yang mudah.

Mari mulakan dengan reka bentuk yang longgar, berdasarkan hanya satu antara muka:

public interface ConnectionPool { Connection getConnection(); boolean releaseConnection(Connection connection); String getUrl(); String getUser(); String getPassword(); }

The ConnectionPool muka mentakrifkan API awam kolam sambungan asas.

Sekarang, mari buat implementasi, yang menyediakan beberapa fungsi asas, termasuk mendapatkan dan melepaskan sambungan gabungan:

public class BasicConnectionPool implements ConnectionPool { private String url; private String user; private String password; private List connectionPool; private List usedConnections = new ArrayList(); private static int INITIAL_POOL_SIZE = 10; public static BasicConnectionPool create( String url, String user, String password) throws SQLException { List pool = new ArrayList(INITIAL_POOL_SIZE); for (int i = 0; i < INITIAL_POOL_SIZE; i++) { pool.add(createConnection(url, user, password)); } return new BasicConnectionPool(url, user, password, pool); } // standard constructors @Override public Connection getConnection() { Connection connection = connectionPool .remove(connectionPool.size() - 1); usedConnections.add(connection); return connection; } @Override public boolean releaseConnection(Connection connection) { connectionPool.add(connection); return usedConnections.remove(connection); } private static Connection createConnection( String url, String user, String password) throws SQLException { return DriverManager.getConnection(url, user, password); } public int getSize() { return connectionPool.size() + usedConnections.size(); } // standard getters }

Walaupun cukup naif, kelas BasicConnectionPool menyediakan fungsi minimum yang kami harapkan dari pelaksanaan penyatuan sambungan khas.

Ringkasnya, kelas menginisialisasi kumpulan sambungan berdasarkan ArrayList yang menyimpan 10 sambungan, yang dapat digunakan dengan mudah.

Ada kemungkinan untuk membuat sambungan JDBC dengan kelas DriverManager dan dengan pelaksanaan Datasource .

Oleh kerana jauh lebih baik untuk mengekalkan penciptaan pangkalan data sambungan secara agnostik, kami telah menggunakan yang pertama, dalam kaedah membuat () kilang statik.

Dalam kes ini, kami telah meletakkan kaedah tersebut dalam BasicConnectionPool , kerana ini adalah satu-satunya pelaksanaan antara muka.

Dalam reka bentuk yang lebih kompleks, dengan pelbagai implementasi ConnectionPool , lebih baik meletakkannya di antara muka, oleh itu mendapatkan reka bentuk yang lebih fleksibel dan tahap kohesi yang lebih tinggi.

Titik yang paling relevan untuk ditekankan di sini adalah bahawa setelah kolam dibuat, sambungan diambil dari kolam, jadi tidak perlu membuat yang baru .

Furthermore, when a connection is released, it's actually returned back to the pool, so other clients can reuse it.

There's no any further interaction with the underlying database, such as an explicit call to the Connection's close() method.

5. Using the BasicConnectionPool Class

As expected, using our BasicConnectionPool class is straightforward.

Let's create a simple unit test and get a pooled in-memory H2 connection:

@Test public whenCalledgetConnection_thenCorrect() { ConnectionPool connectionPool = BasicConnectionPool .create("jdbc:h2:mem:test", "user", "password"); assertTrue(connectionPool.getConnection().isValid(1)); }

6. Further Improvements and Refactoring

Of course, there's plenty of room to tweak/extend the current functionality of our connection pooling implementation.

For instance, we could refactor the getConnection() method, and add support for maximum pool size. If all available connections are taken, and the current pool size is less than the configured maximum, the method will create a new connection.

Also, we could additionally verify whether the connection obtained from the pool is still alive, before passing it to the client.

@Override public Connection getConnection() throws SQLException { if (connectionPool.isEmpty()) { if (usedConnections.size() < MAX_POOL_SIZE) { connectionPool.add(createConnection(url, user, password)); } else { throw new RuntimeException( "Maximum pool size reached, no available connections!"); } } Connection connection = connectionPool .remove(connectionPool.size() - 1); if(!connection.isValid(MAX_TIMEOUT)){ connection = createConnection(url, user, password); } usedConnections.add(connection); return connection; } 

Note that the method now throws SQLException, meaning we'll have to update the interface signature as well.

Or, we could add a method to gracefully shut down our connection pool instance:

public void shutdown() throws SQLException { usedConnections.forEach(this::releaseConnection); for (Connection c : connectionPool) { c.close(); } connectionPool.clear(); }

In production-ready implementations, a connection pool should provide a bunch of extra features, such as the ability for tracking the connections that are currently in use, support for prepared statement pooling, and so forth.

Oleh kerana kami akan mempermudah, kami akan menghilangkan cara melaksanakan ciri tambahan ini dan memastikan pelaksanaannya tidak selamat untuk dijelaskan demi kejelasan.

7. Kesimpulannya

Dalam artikel ini, kami melihat secara mendalam apa penyatuan penyambungan dan belajar bagaimana menggelar pelaksanaan penyatuan sambungan kami sendiri.

Sudah tentu, kita tidak perlu mulai dari awal setiap kali kita mahu menambahkan lapisan penyatuan sambungan yang lengkap ke aplikasi kita.

Itulah sebabnya kami membuat pembundaran ringkas yang menunjukkan beberapa kerangka kumpulan penyambungan yang paling popular, jadi kami dapat mempunyai idea yang jelas tentang cara bekerjasama dengan mereka, dan memilih yang paling sesuai dengan kehendak kami.

Seperti biasa, semua contoh kod yang ditunjukkan dalam artikel ini terdapat di GitHub.