1. مقدمة
تستخدم تقنية التجميع على نطاق واسع في جافا. باختصار ، يستخدم تجمع الكائنات لتخزين مثيل مع عدد محدود من الحالات. يحصل المطورون على مثيلات من تجمع الكائنات ثم يعودون إلى تجمع الكائن بعد الاستخدام ، مما يقلل من النفقات العامة لإنشاء كائنات متكررة للنظام وتدمير الكائنات إلى حد ما. تجمع مؤشرات ترابط Java ومجموعة اتصال قاعدة البيانات هي تطبيقات نموذجية ، ولكن ليست جميع الكائنات مناسبة للتجميع. سيؤثر تجميع الكائنات ذات النفقات العامة الصغيرة نسبيًا على الأداء ، لأن الحفاظ على تجمعات الكائنات يتطلب أيضًا موارد معينة. بالنسبة للكائنات ذات النفقات العامة العالية والمتكررة واستخدام الاستخدام ، فإن استخدام تقنية التجميع سيحسن الأداء بشكل كبير.
هناك العديد من تجمعات اتصال قاعدة البيانات الناضجة في الصناعة ، مثل C3P0 و DBCP و Proxool و Druid's Alibaba. هناك العديد من المصدر المفتوح ، ويمكنك العثور على رمز المصدر على Github. يمكن للمطورين الاختيار بناءً على احتياجاتهم وخصائصهم وأدائهم. هذه المقالة هي فقط لفهم تقنية تجميع التعلم وتنفيذ تجمع اتصال قاعدة بيانات بسيط. إذا كانت هناك أي أخطاء ، آمل أن أنتقدها وتصحيحها.
2. التصميم
الطبقات والواجهات الرئيسية
.ConnectionParam - فئة معلمة تجمع قاعدة البيانات ، المسؤولة عن تكوين اتصالات قاعدة البيانات والمعلمات المتعلقة بتجمع الاتصال. التنفيذ باستخدام المنشئ.
كلمة مرور URL URL - مطلوب للاتصال بقاعدة البيانات
Minconnection - الحد الأدنى لعدد الاتصالات
MaxConnection - الحد الأقصى لعدد الاتصالات
Minidle - الحد الأدنى لعدد اتصالات الخمول
Maxwait - أقصى وقت انتظار
سائق السلسلة النهائي الخاص ؛ عنوان URL الخاص بالسلسلة النهائية ؛ مستخدم سلسلة نهائية خاصة ؛ كلمة مرور السلسلة النهائية الخاصة ؛ نهائي خاص int minconnection ؛ نهائي خاص int maxconnection ؛ نهائي خاص int minidle ؛ نهائي خاص طويل maxwait.
.ConnectionPool - تجمع اتصالات قاعدة البيانات
يتم الإعلان عن مُنشئ ConnectionPool باعتباره الحماية ، ويحظر الإبداع الخارجي ، ويتم إدارته بشكل موحد عن طريق ConnectionPoolFactory.
يقوم ConnectionPool بتنفيذ واجهة DataSource وطريقة REGCONNECTION ().
يحتوي ConnectionPool على حاوين - قائمة انتظار واحدة تخزن اتصال الخمول ويخزن المتجه الآخر (بالنظر إلى المزامنة) الاتصال المستخدم.
عندما يستخدم المطور اتصال قاعدة البيانات ، يتم استرداده من قائمة الانتظار ، وإذا لم يكن هناك ، فإنه يعود فارغًا ؛ عند الانتهاء من الاتصال الوثيق ، يتم إعادته إلى المتجه.
يوفر ConnectionPool آلية توسع ديناميكية بسيطة تعتمد على Minidle و MaxConnection.
initial intern inital_size = 5 ؛ Static Final String leight_method = "Close" ؛ مسجل لوجبر ثابت خاص ؛ حجم الباحث الخاص ؛ ConnectionParam ConnectionParam ؛ arrayblockingqueue الخاص <Nocietion> idleConnectionQueue ؛ المتجه الخاص <Connality> مشغول ConnecteVector ؛
.ConnectionPoolFactory - فئة إدارة تجمعات الاتصال
يحتوي ConnectionPoolFactory على concurrenthashmap ثابت لتخزين كائنات تجمع الاتصال.
يتيح ConnectionPoolFactory إنشاء تجمعات اتصال متعددة مع تكوينات مختلفة لقواعد البيانات المختلفة.
يحتاج المطور إلى تسجيل (ربط) تجمع الاتصال باسم محدد لأول مرة ، ثم الحصول على الاتصال من مجموعة الاتصال المحددة في كل مرة.
إذا لم يعد تجمع الاتصال قيد الاستخدام ، فيمكن للمطور تسجيل الخروج (إلغاء تحديد) تجمع الاتصال.
الخريطة الثابتة الخاصة <string ، connectionPool> poolmap = concurrenthashmap <> () جديد ؛ الاتصال الثابت العام getConnection (boolname) يلقي sqlexception {nameCheck (poolname) ؛ ConnectionPool ConnectionPool = poolmap.get (poolname) ؛ return ConnectionPool.getConnection () ؛ } public static void registerConnectionPool (اسم السلسلة ، connectionParam ConnectionParam) {registerCheck (name) ؛ poolmap.put (الاسم ، اتصال جديد (connectionParam)) ؛ } // دع GC public static void unregisterConnectionPool (اسم السلسلة) {nameCheck (name) ؛ ConnectionPool ConnectionPool = poolmap.get (الاسم) ؛ poolmap.remove (name) ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {connectionPool.clear () ؛}}). start () ؛ }الكود الأساسي
الكود الأساسي لمجموعة اتصال قاعدة البيانات موجود في طريقة getConnection (). عادة ، بعد معالجة المطور عمليات قاعدة البيانات ، سيتم استدعاء طريقة Close (). يجب إغلاق الاتصال ويجب إصدار الموارد. في تجمع اتصال قاعدة البيانات ، عندما يقوم المستخدم باستدعاء طريقة Close () ، يجب عدم إغلاق الاتصال مباشرة ، ولكن يجب إعادته إلى التجمع وإعادة استخدامه. هنا ، يتم استخدام آلية الوكيل الديناميكي Java. لا يعيد GetConnection اتصال "حقيقي" ، ولكن فئة وكيل مخصص (يتم استخدام فئة مجهولة هنا). عندما يقوم المستخدم باستدعاء طريقة Close () ، فإنه يعترض ويعيدها إلى المسبح. بالنسبة للوكيل الديناميكي ، يمكنك الرجوع إلى مدونة أخرى "تطبيق Java Dynamic Proxy Simple Application"
Override Public Connection getConnection () يلقي sqlexception {try {final connection connection = idleConnectionQueue.poll (connectionParam.getMaxWait () ، timeUnit.milliseconds) ؛ if (connection == null) {logger.info (fmareMsg ()) ؛ insureCapacity () ؛ العودة لاغية. } مشغول ConnectionVector.add (اتصال) ؛ return (اتصال) proxy.newproxyinstance (this.getClass (). args) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } إرجاع فارغ ؛ }2. الاستخدام
أولاً ، يقوم المستخدم بإنشاء معلمات تجمع اتصالات قاعدة البيانات (ConnectionParam) ، بما في ذلك برنامج التشغيل ، URL ، المستخدم ، كلمة المرور المطلوبة. يمكنك تخصيص خيارات مثل MinConnection ، MaxConnection ، إلخ. إذا لم يتم تعيينها ، استخدم القيمة الافتراضية للنظام. هذا هو فائدة استخدام البناء لبناء عدد كبير من السمات ، بما في ذلك السمات المطلوبة والسمات الاختيارية. ثم قم بتسجيل تجمع الاتصال مع ConnectionPoolFactory باسم محدد ، وأخيراً احصل على الاتصال من خلال استدعاء طريقة المصنع الثابت ConnectionPoolFactory.
String driver = "com.mysql.jdbc.driver" ؛ url url = "jdbc: mysql: // localhost: 3306/test" ؛ string user = "root" ؛ سلسلة كلمة المرور = "الجذر" ؛ ConnectionParam ConnectionParam = New ConnectionParam.ConnectionParambuilder (برنامج التشغيل ، URL ، المستخدم ، كلمة المرور) .build () ؛ ConnectionPoolFactory.registerConnectionPool ("Test" ، ConnectionParam) ؛ اتصال الاتصال = connectionPoolFactory.getConnection ("Test") ؛3. الكود
.ParamConfiguration
Package Database.Config ؛ import java.io.serializable ؛/** * معلمات اتصال قاعدة البيانات * تم إنشاؤها بواسطة Michael Wong في 2016/1/18. */Public Class ParamConfiguration تنفذ serializable {public static final int min_connection = 5 ؛ الثابت العام النهائي int max_connection = 50 ؛ استاتيكي عام نهائي int min_idle = 5 ؛ نهائي ثابت طويل الطويل max_wait = 30000 ؛ paramConfiguration () {}}.Builder
Package Database ؛/** * Builder * الذي تم إنشاؤه بواسطة Michael Wong في 2016/1/18. */باني الواجهة العامة <T> {t build () ؛}.connectionparam
Database Package ؛ Import Database.Config.ParamConfiguration ؛/** * معلمات اتصال قاعدة البيانات * تم إنشاؤها بواسطة Michael Wong في 2016/1/18. */فئة عامة ConnectionParam {Private Final String Driver ؛ عنوان URL الخاص بالسلسلة النهائية ؛ مستخدم سلسلة نهائية خاصة ؛ كلمة مرور السلسلة النهائية الخاصة ؛ نهائي خاص int minconnection ؛ نهائي خاص int maxconnection ؛ نهائي خاص int minidle ؛ نهائي خاص طويل maxwait. Private ConnectionParam (ConnectionParambuilder Builder) {this.driver = builder.driver ؛ this.url = builder.url ؛ this.user = builder.user ؛ this.password = builder.password ؛ this.minconnection = builder.minconnection ؛ this.maxConnection = builder.maxConnection ؛ this.minidle = builder.minidle ؛ this.maxwait = builder.maxwait ؛ } السلسلة العامة getDriver () {return this.driver ؛ } السلسلة العامة geturl () {return this.url ؛ } السلسلة العامة getUser () {return this.user ؛ } السلسلة العامة getPassword () {return this.password ؛ } public int getMinConnection () {return this.minconnection ؛ } public int getMaxConnection () {return this.maxConnection ؛ } public int getMinidle () {return this.minidle ؛ } public long getMaxWait () {return this.maxwait ؛ } Public Static Classe ConnectionParambuilder تنفذ منشئ <ConnalitParam> {// المعلمات المطلوبة برنامج تشغيل السلسلة النهائية الخاصة ؛ عنوان URL الخاص بالسلسلة النهائية ؛ مستخدم سلسلة نهائية خاصة ؛ كلمة مرور السلسلة النهائية الخاصة ؛ // معلمات اختيارية - تهيئتها إلى القيمة الافتراضية int minconnection = paramConfiguration.min_connection ؛ private int maxConnection = paramConfiguration.max_connection ؛ private int minidle = paramConfiguration.min_idle ؛ // الحصول على وقت الانتظار وقت الانتظار الخاص الطويل maxwait = paramConfiguration.max_wait ؛ Public ConnectionParambuilder (برنامج تشغيل السلسلة ، عنوان URL للسلسلة ، مستخدم سلسلة ، كلمة مرور السلسلة) {this.driver = driver ؛ this.url = url ؛ this.user = المستخدم ؛ this.password = كلمة المرور ؛ } Public ConnectionParambuilder minconnection (int minconnection) {this.minconnection = minConnection ؛ إرجاع هذا ؛ } public connectionParambuilder maxConnection (int maxConnection) {this.maxConnection = maxConnection ؛ إرجاع هذا ؛ } public connectionParambuilder minidle (int minidle) {this.minidle = minidle ؛ إرجاع هذا ؛ } public connectionParambuilder maxwait (int maxwait) {this.maxwait = maxwait ؛ إرجاع هذا ؛ } Override Public ConnectionParam Build () {return New ConnectionParam (this) ؛ }}}.connectionpool
package database.factory ؛ import database.connectionparam ؛ import javax.sql.datasource ؛ import java.io.printWriter ؛ import java.lang.reflect.invocationhandler ؛ import java.lang.reflect.method java.sql.drivermanager ؛ استيراد java.sql.sqlexception ؛ استيراد java.sql.sqlfeatuRenotsupportedException ؛ import java.util.vector ؛ import java.util.concurrent.arrayBlockingeue java.util.logging.logger ؛/** * مجموعة اتصال * تم إنشاؤها بواسطة Michael Wong في 2016/1/18. */connection public connectionpool ينفذ DataSource {private static final inter_size = 5 ؛ Static Final String leight_method = "Close" ؛ مسجل لوجبر ثابت خاص ؛ حجم الباحث الخاص ؛ ConnectionParam ConnectionParam ؛ arrayblockingqueue الخاص <Nocietion> idleConnectionQueue ؛ المتجه الخاص <Connality> مشغول ConnecteVector ؛ ConnectionPool المحمي (ConnectionParam ConnectionParam) {this.connectionParam = connectionParam ؛ int maxConnection = connectionParam.getMaxConnection () ؛ idleConnectionQueue = جديد arrayblockingqueue <> (maxConnection) ؛ مشغول ConnectionVector = ناقل جديد <> () ؛ logger = logger.getLogger (this.getClass (). getName ()) ؛ initConnection () ؛ } private void initConnection () {int minConnection = connectionParam.getMinConnection () ؛ int ignsize = initial_size <minconnection؟ Minconnection: initial_size ؛ حاول {class.forname (connectionParam.getDriver ()) ؛ لـ (int i = 0 ؛ i <initialsize+connectionParam.getMinConnection () ؛ i ++) {idleConnectionQueue.put (newConnection ()) ؛ حجم ++ ؛ }} catch (استثناء e) {رمي استثناء جديد initializerError (e) ؛ }} Override public connection getConnection () يلقي sqlexception {try {final connection = idleconnectionqueue.poll (connectionParam.getMaxWait () ، timeUnit.milliseconds) ؛ if (connection == null) {logger.info (fmareMsg ()) ؛ insureCapacity () ؛ العودة لاغية. } مشغول ConnectionVector.add (اتصال) ؛ return (اتصال) proxy.newproxyinstance (this.getClass (). args) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } إرجاع فارغ ؛ } اتصال خاص newConnection () يلقي sqlexception {String url = connectionParam.geturl () ؛ string user = connectionParam.getuser () ؛ سلسلة كلمة المرور = connectionParam.getPassword () ؛ إرجاع drivermanager.getConnection (url ، المستخدم ، كلمة المرور) ؛ } محمية int size () {size return ؛ } محمي int idleConnectionQuantity () {return idleConnectionQueue.size () ؛ } محمي int laveConnectionQuantity () {return upplyConnectionVector.size () ؛ } private void insureCapacity () يلقي sqlexception {int minidle = connectionParam.getMinIdle () ؛ int maxConnection = connectionParam.getMaxConnection () ؛ int newCapacity = size + minidle ؛ NewCapacity = NewCapacity> MaxConnection؟ MaxConnection: NewCapacity ؛ int growcount = 0 ؛ if (size <newCapacity) {try {for (int i = 0 ؛ i <newCapacity - size ؛ i ++) {idleConnectionQueue.put (newConnection ()) ؛ GrowCount ++ ؛ }} catch (interruptedException e) {E.PrintStackTrace () ؛ }} size = size + growcount ؛ } void clear clear () {try {while (size--> 0) {connection connection = idleConnectionQueue.take () ؛ connection.close () ؛ }} catch (interruptedException | sqlexception e) {E.PrintStackTrace () ؛ }} private string murplemsg () {return "Database مشغول ، يرجى الانتظار ..." ؛ } Override Public Connection getConnection (اسم المستخدم ، كلمة مرور السلسلة) يلقي sqlexception {return null ؛ } Override Public PrintWriter getLogWriter () يلقي sqlexception {return null ؛ } Override public void setLogWriter (printWriter out) يلقي sqlexception {} Override public void setLogIntimeOut (int seconds) يلقي sqlexception {} Override public int getLogIntimeOut () يلقي sqlexception {return 0 ؛ } Override public logger getParentLogger () يلقي sqlfeaturenotsupportedException {return null ؛ } Override public <T> t unf unf (class <t> iface) يرمي sqlexception {return null ؛ } Override Public Boolean Iswrapperfor (class <؟> iface) يلقي sqlexception {return false ؛ }}.connectionPoolFactory
package database.factory ؛ import database.connectionparam ؛ import java.sql.connection ؛ import java.sql.sqlexception ؛ import java.util.map ؛ import java.util.concurrent.concurrenthashmap ؛ */classe connectionpoolfactory {private connectionPoolFactory () {} خريطة ثابتة خاصة <string ، connectionPool> poolmap = concurrenthashmap <> () جديد ؛ الاتصال الثابت العام getConnection (boolname) يلقي sqlexception {nameCheck (poolname) ؛ ConnectionPool ConnectionPool = poolmap.get (poolname) ؛ return ConnectionPool.getConnection () ؛ } public static void registerConnectionPool (اسم السلسلة ، connectionParam ConnectionParam) {registerCheck (name) ؛ poolmap.put (الاسم ، اتصال جديد (connectionParam)) ؛ } // دع GC public static void unregisterConnectionPool (اسم السلسلة) {nameCheck (name) ؛ ConnectionPool ConnectionPool = poolmap.get (الاسم) ؛ poolmap.remove (name) ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {connectionPool.clear () ؛}}). start () ؛ } Size int static public (boolname) {nameCheck (poolname) ؛ return poolmap.get (poolname) .size () ؛ } int static int getIdleConnectionQuantity (سلسلة poolname) {nameCheck (poolname) ؛ return poolmap.get (poolname) .IdleConnectionQuantity () ؛ } int static int getBusyConnectionQuantity (سلسلة poolname) {nameCheck (poolname) ؛ return poolmap.get (poolname) .busyConnectionQuantity () ؛ } private static void registerCheck (اسم السلسلة) {if (name == null) {رمي new alfictalargumentException (nullName ()) ؛ }} private static void nameCheck (اسم السلسلة) {if (name == null) {رمي new intervalialArgumentException (nullName ()) ؛ } if (! poolmap.containskey (name)) {رمي new alficalArgumentException (notexists (name)) ؛ }} سلسلة ثابتة خاصة nullName () {return "يجب ألا يكون اسم البلياردو فارغًا" ؛ } سلسلة ثابتة خاصة (اسم سلسلة) {إرجاع "تجمع الاتصال المسماة" + name + "لا يوجد" ؛ }} 4. اختبار
اختبار وحدة جونيت
package database.factory ؛ استيراد database.connectionparam ؛ استيراد org.junit.test ؛ استيراد java.sql.connection ؛ استيراد java.sql.sqlexpiste ؛ import java.util.arraylist ؛ exprice java.util.lit 2016/1/20. */Classe ConnectionPoolFactoryTest {test public void testgetConnection () يلقي sqlexception {string driver = "com.mysql.jdbc.driver" ؛ url url = "jdbc: mysql: // localhost: 3306/test" ؛ string user = "root" ؛ سلسلة كلمة المرور = "الجذر" ؛ ConnectionParam ConnectionParam = New ConnectionParam.ConnectionParambuilder (برنامج التشغيل ، URL ، المستخدم ، كلمة المرور) .build () ؛ ConnectionPoolFactory.registerConnectionPool ("Test" ، ConnectionParam) ؛ قائمة <ConnateS> ConnectionList = new ArrayList <> () ؛ لـ (int i = 0 ؛ i <12 ؛ i ++) {connectionList.add (connectionPoolFactory.getConnection ("test")) ؛ } مطبعة()؛ Close (ConnectionList) ؛ مطبعة()؛ ConnectionList.clear () ؛ لـ (int i = 0 ؛ i <12 ؛ i ++) {connectionList.add (connectionPoolFactory.getConnection ("test")) ؛ } مطبعة()؛ Close (ConnectionList) ؛ ConnectionPoolFactory.unregisterConnectionPool ("Test") ؛ } test (متوقع = elevalalArgumentException.class) public void testException () {try {connectionPoolFactory.getConnection ("test") ؛ } catch (sqlexception e) {E.PrintStackTrace () ؛ }} إغلاق void الخاص (قائمة <ConnateS> Connection) يلقي sqlexception {for (connection conn: connectionlist) {if (conn! = null) {conn.close () ؛ }}} private void print () {system.out.println ("idle:" + connectionPoolFactory.getIdleConnectionQuantity ("test")) ؛ System.out.println ("مشغول:" + connectionPoolFactory.getBusyConnectionQuantity ("test")) ؛ System.out.println ("الحجم:" + connectionPoolFactory.size ("test")) ؛ }}ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون مفيدًا لتعلم الجميع.