В одном из проектов на яве мне понадобился connection pool, без него
соединения с базой получались слишком частые. После определенного
поиска обнаружил DBCP от apache и
статью на винграде
DBCP мне не очень понравился, дело вкуса, конечно, но слишком много
зависимостей. Да и тяжеловат он для мелкого проекта. Vingrad-овский
connection pool – делает немного не то что хотелось кэширует
statements.. И отсутствует обработка случаев, когда, к примеру база
умрет (на время)..
Критика без конкретных предложений – критиканство. Так что взял и
написал как нравится самому. Вот что получилось:
Тут все просто, берем экземпляр connectionpool-а (проект небольшой,
так что оформим его в виде singleton, устанавливаем параметры, и
если не удалось инициализировать, то не судьба и программу
выполнять…
И дальше, в том месте где используется соединение, что-то вроде:
Тут, если посмотреть, то connection берется не из экземпляра
connection pool, а из статического метода в классе. И таким же образом
отдается. Если у нас connection pool в виде singleton – зачем
переливать из пустого в порожнее, брать по очереди connection pool и
затем из него connection? Взять экземпляр connection pool я могу и сам
в статическом методе. Мелочь, но писать программы проще…
importjava.sql.Connection;importjava.util.Queue;importjava.util.concurrent.ConcurrentLinkedQueue;importjava.util.logging.Logger;publicclassConnectionPool{privatestaticConnectionPoolinstance;privateConnectionPool(){connections=newConcurrentLinkedQueue<connection>();}publicstaticConnectionPoolgetInstance(){if(instance==null)instance=newConnectionPool();returninstance;}publicstaticConnectiongetConnection(){returngetInstance().getInstanceConnection();}publicstaticvoidreleaseConnection(Connectionc){getInstance().releaseInstanceConnection(c);}/** * Выделить соединение из пула. * логика такая: * если не пустой - взять соединение из пула, проверить на валидность * и вернуть. невалидные соединения сразу убиваем. * если пул пустой - сделать новое соединение и вернуть его. Если не смогли * сделать новое соединение (типа DB сервер умер) - просто жалуемся в лог * и возвращаем null */privateConnectiongetInstanceConnection(){if(!alreadyInitialized){log.severe("improper using of db pool (getConnection without init)");returnnull;};ConnectioncurrentConnection;do{currentConnection=connections.poll();if(currentConnection==null)break;//пустой пул// если дохлое соединение - не будем его использоватьif(!validConnection(currentConnection)){currentConnection=null;}}while(currentConnection==null);try{if(connections.size()==0){log.info("DB Pool depleted, add new connection");connections.add(java.sql.DriverManager.getConnection(path,userName,password));currentConnection=connections.poll();if(!validConnection(currentConnection)){// не судьба. СдаемсяthrownewException("new connection also is not valid");}}}catch(Exceptionie){log.severe("cannot provide sql connection:"+ie);returnnull;};returncurrentConnection;}privatevoidreleaseInstanceConnection(Connectionc){if(!alreadyInitialized){log.severe("improper using of db pool (releseConnection without init)");return;};if(getCurrentSize()>=getMaxPoolSize()){try{c.close();}catch(Exceptione){};}elseif(!validConnection(c)){// Не будем возвращать в пул дохлое соединение}else{connections.offer(c);}}privateintmaxPoolSize=5;publicintgetMaxPoolSize(){returnmaxPoolSize;}publicvoidsetPoolsize(intmaxPoolSize){if(!alreadyInitialized){this.maxPoolSize=maxPoolSize;}else{log.severe("setPoolsize after init. ignoring");}}publicintgetCurrentSize(){returnconnections.size();}privateStringuserName;privateStringpassword;privateStringpath;privateStringclassName;publicStringgetUserName(){returnuserName;}publicvoidsetUsername(StringuserName){if(!alreadyInitialized){this.userName=userName;}else{log.severe("setUserName after init. ignoring");}}publicStringgetPassword(){returnpassword;}publicvoidsetPassword(Stringpassword){if(!alreadyInitialized){this.password=password;}else{log.severe("setPassword after init. ignoring");}}publicStringgetPath(){returnpath;}publicvoidsetPath(Stringpath){if(!alreadyInitialized){this.path=path;}else{log.severe("Path after init. ignoring");}}publicStringgetClassName(){returnclassName;}publicvoidsetClassName(StringclassName){if(!alreadyInitialized){this.className=className;}else{log.severe("setClassName after init. ignoring");}}/** * init - Инициализация пула сообщений * @return boolean - успешно ли инициализирован пул (true - все хорошо) */publicsynchronizedbooleaninit(){log.entering("ConnectionPool","init");try{if(alreadyInitialized){thrownewException("Double init of db pool");}Class.forName(this.className);for(inti=1;i<=5;i++){connections.add(java.sql.DriverManager.getConnection(path,userName,password));};alreadyInitialized=true;log.exiting("ConnectionPool","init");returntrue;}catch(Exceptione){log.severe("Cannot init db pool:"+e);}log.exiting("ConnectionPool","init");returnfalse;}privatestaticLoggerlog=Logger.getLogger("dbpool");/** * Хранилище для коннекций */privateConcurrentLinkedQueue<connection>connections;/** * переменная предотвращающая двойную инициализацию DB pool */privatebooleanalreadyInitialized=false;/** * Проверка что соединение до сих пор живо. Если есть проблемы, мы его * просто убьем * * Проверяем просто делая setAutoCommit. Если никаких exceptions * не последовало, считаем что все в порядке. */privatebooleanvalidConnection(java.sql.Connectionc){try{booleanstatus=c.getAutoCommit();c.setAutoCommit(!status);c.setAutoCommit(status);}catch(Exceptione){try{c.close();}catch(ExceptionnotImportant){};log.info("Dead sql connection detected");returnfalse;}returntrue;}}
Несколько попутных замечаний: Держится 5 соединений (по умолчанию, может быть
изменено в setPoolsize() ), если программа просит больше соединений, будут
устанавливаться новые соединения с базой. Просто они будут закрываться, когда в
них пропадет нужда. Так что poolsize – это сколько соединений будет повторно
использоваться.