I was asked in an interview the below question.
The code given to me is
interface Connection {
void execute();
void close();
}
interface ConnectionPool {
Connection getConnection();
}
class Client {
ConnectionPool connectionPool = new MyConnectionPool() ;
Connection connection = connectionPool.getConnection();
public void execute() {
try{
connection.execute();
}finally {
connection.close();
}
}
}
class MyConnectionPool implements ConnectionPool {
MyConnectionPool(List<Connection> connections) {
}
#Override
public Connection getConnection() {
return null;
}
}
Here the implementation of the Connection is done by somebody else, I can only change the MyConnection pool class or create new classes. The issue is whenever the client call connection.close, the connection object is actually closed, the requirement is instead it should be added back to the connection pool which somebody else can use.
My implementation is like
class MyConnectionPool implements ConnectionPool {
Queue<Connection> connections;
MyConnectionPool(List<Connection> connections) {
this.connections = new LinkedList(connections);
}
#Override
public Connection getConnection() {
MyConnection connection = new MyConnection(connections.poll(), this);
return connection;
}
public void closeConnection(Connection connection) {
connections.add(connection);
}
}
class MyConnection implements Connection {
Connection connection;
boolean isClosed = false;
MyConnectionPool connectionPool;
MyConnection(Connection connection, MyConnectionPool connectionPool) {
this.connection = connection;
}
#Override
public void execute() {
if (!isClosed){
connection.execute();
}
}
#Override
public void close() {
if (!isClosed) {
connectionPool.closeConnection(this);
}
}
}
Is it correct ? Is there a better way to achieve the same.
Related
I used singleton pattern to get jdbc connection for my standalone application. The code is as following.
public static synchronized MysqlConnect getDbCon() {
if ( db == null ) {
db = new MysqlConnect();
}
return db;
}
But I saw in many discussions singleton in not good for getting connection. Is it true?
And the suggested using connection pooling. Can anyone give me a good implementation of connection pooling to use instead of the above code?
Here's a simple, singleton based implementation of a connection pool. This connection pool supports getting a connection using either the DriverManager interface or via a DataSource (JNDI).
I use this connection pool for some of my projects (though the actual implementation is slightly more complex)
public class ConnectionPool{
private String jdbcDriver;
private String jdbcURL;
private String user;
private String password;
private int connectionCount=10;
private List<Connection> connections;
private static boolean loadFromDataSource;
private static DataSource dataSource;
private static ConnectionPool connPool;
private ConnectionPool() throws EasyORMException{}
private void setConnection(String jdbcDriver, String jdbcURL, String user, String password,String dbDataSource) throws EasyORMException{
this.jdbcDriver = jdbcDriver;
this.jdbcURL=jdbcURL;
this.user=user;
this.password=password;
connections= new ArrayList<Connection>();
connections.add((ConnectionPool.loadFromDataSource) ? getConnectionFromDataSource() : getConnection());
}
static ConnectionPool getInstance(){
return connPool;
}
private static ConnectionPool getInstanceFromJndi(String propertyFile,boolean loadFromJndi) throws EasyORMException{
ConnectionProp cp=readPropFromFile(propertyFile);
if(loadFromJndi){
dataSource=createDatasource(cp.getDataSource());
loadFromDataSource=true;
}
return ConnectionPool.createConnectionPool(cp.getJdbcDriver(),cp.getDbURL(), cp.getUsername(), cp.getPassword(),cp.getDataSource())
}
public static ConnectionPool getInstance(String propertyFile,boolean loadFromJndi) throws EasyORMException{
return ConnectionPool.getInstanceFromJndi(propertyFile, loadFromJndi, false);
}
public static ConnectionPool getInstance(ConnectionProp cp) throws EasyORMException{
return ConnectionPool.createConnectionPool(cp.getJdbcDriver(),cp.getDbURL(), cp.getUsername(), cp.getPassword(),cp.getDataSource());
}
public static ConnectionPool getInstance(String jndiName) throws EasyORMException{
dataSource=createDatasource(jndiName);
loadFromDataSource=true;
return ConnectionPool.createConnectionPool(null,null, null, null,jndiName);
}
public static ConnectionPool getInstance(String jdbcDriver, String jdbcURL, String user, String password) throws EasyORMException{
return ConnectionPool.createConnectionPool(jdbcDriver,jdbcURL, user, password,null);
}
private static ConnectionPool createConnectionPool(String jdbcDriver, String jdbcURL, String user, String password,String dbDataSource) throws EasyORMException{
if(connPool==null) {
connPool = new ConnectionPool();
connPool.setConnection(jdbcDriver, jdbcURL, user, password, dbDataSource);
}
return connPool;
}
synchronized Connection getAvailableConnection() throws EasyORMException {
Connection conn=null;
int connSize = connections.size();
if(connSize>0){
conn=connections.remove(connSize-1);
}else{
if(connSize<connectionCount){
for(int i=0;i<initialConnCount;i++)
conn=(ConnectionPool.loadFromDataSource)?getConnectionFromDataSource() :getConnection();
}else{
throw new EasyORMException(EasyORMException.CONNECTION_NUM_EXCEEDED);
}
}
return conn;
}
synchronized void returnConnection(Connection conn){
connections.add(conn);
}
private Connection getConnection() throws EasyORMException {
Connection conn=null;
try {
Class.forName(jdbcDriver);
conn = DriverManager.getConnection(jdbcURL, user, password);
} catch (ClassNotFoundException e) {
throw new EasyORMException(e);
} catch (SQLException e) {
throw new EasyORMException(e);
}
return conn;
}
private Connection getConnectionFromDataSource() throws EasyORMException {
try{
return dataSource.getConnection();
}catch(SQLException e){
throw new EasyORMException(e);
}
}
public void setNumberOfConnections(int count){
this.connectionCount=count;
}
public int getNumberOfConnections(){
return connectionCount;
}
private static DataSource createDatasource(String jndiDb)throws EasyORMException{
InitialContext initCtx=null;
try {
initCtx = new InitialContext();
return (DataSource)initCtx.lookup("java:comp/env/"+jndiDb);
} catch (NamingException e) {
throw new EasyORMException(e);
}finally{
if(initCtx!=null){
try {
initCtx.close();
} catch (NamingException e) {
throw new EasyORMException(e);
}
}
}
}
}
Some methods don't have public access (because they're written as part of a library) but you can change them to public if you need to.
I have this class (my database connection factory):
#ApplicationScoped
public class ConnectionFactory {
private ComboPooledDataSource datasource;
private Long open = 0l;
private Long close = 0l;
#PostConstruct
public void init() throws PropertyVetoException, SQLException {
datasource = new ComboPooledDataSource();
datasource.setDriverClass("org.postgresql.Driver");
datasource.setJdbcUrl("jdbc:postgresql:dbcampanha");
datasource.setUser("postgres");
datasource.setPassword("admin");
datasource.setMinPoolSize(1);
datasource.setMaxPoolSize(5);
datasource.setCheckoutTimeout(30000);
datasource.setUnreturnedConnectionTimeout(30);
datasource.setMaxIdleTime(30);
datasource.setDebugUnreturnedConnectionStackTraces(true);
datasource.setAcquireIncrement(1);
}
#Produces
#RequestScoped
public Connection getConnection() throws ClassNotFoundException {
open++;
try {
Connection connection = datasource.getConnection();
connection.setAutoCommit(false);
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close(#Disposes Connection connection) {
close++;
try {
connection.commit();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Long getOpenedConnectionCounter() {
return open;
}
public Long getClosedConnectionCounter(){
return close;
}
public ComboPooledDataSource getDatasource(){
return datasource;
}
}
I use this class with an JAX-RS application. And for some tests using this route:
#RequestScoped
#Path("/test")
public class TesteService {
#Inject
private Connection connection;
#GET
#Produces(MyMediaType.JSON)
#Path("/yes")
public Response success() throws SQLException {
connection.getClientInfo("");
return Response.ok().build();
}
}
And this class for my Client:
public class TesteMain {
private static final String prefix = "http://localhost:8080/schoolwork/service/test/";
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000; i++) {
Request request = new Request(prefix + "yes");
request.start();
if(i % 10 == 0)
Thread.sleep(1000l);
}
}
public static class Request extends Thread {
private String rota;
public Request(String rota){
this.rota = rota;
}
#Override
public void run() {
try {
HttpURLConnection url = (HttpURLConnection) (new URL(rota).openConnection());
url.connect();
System.out.println(url.getResponseCode() == 200 ? "SUCCESS" : "ERROR");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I receive this infos:
{
"opened-connection": 789,
"closed-connection": 867,
}
Yes, I have a number of closed database connection greater than opened. HOW? Any idea for this?
I use
Tomcat 7 + Java 7
P.S. I am sorry for my bad English :/
SOLVED
I change my counters for AtomicInteger objects, and works perfectly.
So, two quick comments:
Your counters are (boxed) longs read and updated willy-nilly by 1000 concurrent Threads. Their values will in general be unpredictable and nondeterministic. They certainly won't accurately count what you intend them to count. Consider using atomic operations on AtomicLongs instead.
Your commit() (or rollback()) should be attached to your database business logic, the part where you can tell a unit of work has either succeeded or failed. You shouldn't automatically commit on close.
This class is the only thing that should ever access the database and I'd like to give it a single connection object when my application starts up. However, instantiation may cause an exception to be thrown, so I can't do this:
public class DBManager {
private static Connection conn = Database.getReadOnlyConnection();
...
...
}
I have a pretty ugly workaround, and I was wondering if there's a better way.
public class DBManager {
private static Connection conn = null;
private static DBManager instance = null;
public static DBManager getInstance() throws SQLException, ClassNotFoundException {
if (instance == null){
instance = new DBManager();
}
return instance;
}
private DBManager() throws SQLException, ClassNotFoundException {
conn = Database.getReadOnlyConnection();
}
...
...
}
There's also this alternative:
public class DBManager {
private static Connection conn = null;
public static void setConnection(Connection conn) throws NotSupportedException{
if (conn == null){
this.conn = conn;
}
else {
throw new NotSupportedException();
}
}
...
...
}
Is there a nicer way of handling this in Java?
Update
I've decided to use a static initializer block.
public class DBManager {
private static Connection conn;
static {
try {
conn = getReadOnlyConnection();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
...
...
}
This causes the application to fail early if the database cannot be reached and avoids the problem of having to ensure that the DBManager is only being used via an instance.
You program should be more robust - sure you may have a connection now, but what happens if the DB does down - use the same logic for no DB now as DB goes down later
How about a static method and checking if the connection is still valid,
public class Config{
private static Connection con;
private String dbUrl = "jdbc:mysql:/localhost:3306/dbname","username","password";
public static Connection getConnection() throws SQLException{
if(con== null){
con = DriverManager.getConnection(dbUrl);
}
if(!con.isValid(10)){
con = DriverManager.getConnection.getConnection(dbUrl);
}
return con;
}
}
I've got class with one static method
makeConnection
The method returns Connection object for further JDBC operations. Is there a possibility, to create a global Connection field, with result of this method "return" ? I would like to use this field wherever I need.
public class Connection
{
public static Connection makeConnection() throws IOException, SQLException
{
try
{
Class.forName("org.postgresql.Driver");
Properties props = new Properties();
FileInputStream in = new FileInputStream("dataBase.properties");
props.load(in);
in.close();
String drivers = props.getProperty("jdbc.drivers");
if(drivers != null) System.setProperty("jdbc.drivers", drivers);
String url = props.getProperty("jdbc.url");
String username = props.getProperty("jdbc.username");
String password = props.getProperty("jdbc.password");
return DriverManager.getConnection(url, username,password);
}
catch (ClassNotFoundException e)
{
return null;
}
catch(IOException e)
{
return null;
}
catch(SQLException e)
{
return null;
}
}
}
It is possible, but the connection factory is better than a connection.
However, static variable is not a good idea for the lifecycle control of connections.
A good connection pool will take care many problem for you, such as the concurrent accessing, timed out detecting, recycle the alive connections, purging dead connections automatically.
You can do this:
class Foo {
public static Connection conn = bar();
private static Connection bar() {
...
}
}
Is that what you want?
This is not the way to handle connections ... but it might give you an idea how to resolve similar kinds of problems:
public class Wombat
{
public static Wombat getWombat ()
{
if (theWombat == null)
theWombat = new Wombat ();
return theWombat;
}
private static Wombat theWombat= null;
}
You can initialize a static variable in a static initializer block:
class Foo {
public static Connection conn;
static {
try {
conn = makeConnection();
} catch(...) {
...
}
}
}
Need code to create the connection pool in java?
How does we make sure that connection pool doesn't return the same object which is already in use?
How happens if client closed the connection after taking it out from Connection pool?
Update 1:
I want to create this in Simple Java terms and want to see how it works in Multithreading Env. I mean which methods would be synchronized and which are not. Also will this class would be a public class? If yes then any one can access this class and reinitialize the connection pool?
Update 2:
I have some code as below. But i don't how "Closing a connection coming from a pool returns it to the pool, it doesn't close the connection physically."
Also i didn't understood this "Because if a connection has been borrowed from the pool and not returned yet, it's not "available" and can't be redistributed to another client of the pool."
import java.util.*;
import java.sql.*;
class ConnectionPoolManager
{
String databaseUrl = "jdbc:mysql://localhost:3306/myDatabase";
String userName = "userName";
String password = "userPass";
Vector connectionPool = new Vector();
public ConnectionPoolManager()
{
initialize();
}
public ConnectionPoolManager(
//String databaseName,
String databaseUrl,
String userName,
String password
)
{
this.databaseUrl = databaseUrl;
this.userName = userName;
this.password = password;
initialize();
}
private void initialize()
{
//Here we can initialize all the information that we need
initializeConnectionPool();
}
private void initializeConnectionPool()
{
while(!checkIfConnectionPoolIsFull())
{
System.out.println("Connection Pool is NOT full. Proceeding with adding new connections");
//Adding new connection instance until the pool is full
connectionPool.addElement(createNewConnectionForPool());
}
System.out.println("Connection Pool is full.");
}
private synchronized boolean checkIfConnectionPoolIsFull()
{
final int MAX_POOL_SIZE = 5;
//Check if the pool size
if(connectionPool.size() < 5)
{
return false;
}
return true;
}
//Creating a connection
private Connection createNewConnectionForPool()
{
Connection connection = null;
try
{
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(databaseUrl, userName, password);
System.out.println("Connection: "+connection);
}
catch(SQLException sqle)
{
System.err.println("SQLException: "+sqle);
return null;
}
catch(ClassNotFoundException cnfe)
{
System.err.println("ClassNotFoundException: "+cnfe);
return null;
}
return connection;
}
public synchronized Connection getConnectionFromPool()
{
Connection connection = null;
//Check if there is a connection available. There are times when all the connections in the pool may be used up
if(connectionPool.size() > 0)
{
connection = (Connection) connectionPool.firstElement();
connectionPool.removeElementAt(0);
}
//Giving away the connection from the connection pool
return connection;
}
public synchronized void returnConnectionToPool(Connection connection)
{
//Adding the connection from the client back to the connection pool
connectionPool.addElement(connection);
}
public static void main(String args[])
{
ConnectionPoolManager ConnectionPoolManager = new ConnectionPoolManager();
}
}
Need code to create the connection pool in java?
Not sure what the question is but don't create yet another connection pool, use an existing solution like C3P0, Apache DBCP, Proxool or BoneCP (a new player in that field). I would use C3P0.
How does we make sure that connection pool doesn't return the same object which is already in use?
Because if a connection has been borrowed from the pool and not returned yet, it's just not in the pool and can't be assigned to another client of the pool (resources are removed from the pool until they are returned).
How happens if client closed the connection after taking it out from Connection pool?
The connection a client gets from a pool is not really a java.sql.Connection, it's a wrapper (a proxy) for a java.sql.Connection that customizes the behavior of some methods. The close() method is one of them and does not close the Connection instance but returns it to the pool.
Don't write your own. There are plenty of librarires out there that will do this for you that are open source and easy to use and will have solved all the problems you'll encounter trying to make it yourself.
Here is a simple example that uses Apache's Commons DBCP and Commons Pool:
First set up a DataSource.
javax.sql.DataSource source = new org.apache.commons.dbcp.BasicDataSource();
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUsername("username");
source.setPassword("password");
source.setUrl("jdbc:mysql://localhost:3306/myDatabase");
Once you have a DataSource, it is easy to get a connection from the pool.
java.sql.Connection connection = source.getConnection();
closing the connection will return it to the pool for you.
connection.close();
I hope this source code helps
http://jagadeeshmanne.blogspot.com/2014/03/connection-pool-in-java-jdbc.html
Configuration.java
package com.jmanne.utils;
public class Configuration {
public String DB_USER_NAME ;
public String DB_PASSWORD ;
public String DB_URL;
public String DB_DRIVER;
public Integer DB_MAX_CONNECTIONS;
public Configuration(){
init();
}
private static Configuration configuration = new Configuration();
public static Configuration getInstance(){
return configuration;
}
private void init(){
DB_USER_NAME = "root"
DB_PASSWORD = "root"
DB_URL = "jdbc:mysql://localhost:3306/jmanne"
DB_DRIVER = "com.mysql.jdbc.Driver"
DB_MAX_CONNECTIONS = 5
}
}
JdbcConnectionPool.java
package com.jmanne.db;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.jmanne.utils.Configuration;
import com.mysql.jdbc.Connection;
public class JdbcConnectionPool {
List<connection> availableConnections = new ArrayList<connection>();
public JdbcConnectionPool()
{
initializeConnectionPool();
}
private void initializeConnectionPool()
{
while(!checkIfConnectionPoolIsFull())
{
availableConnections.add(createNewConnectionForPool());
}
}
private synchronized boolean checkIfConnectionPoolIsFull()
{
final int MAX_POOL_SIZE = Configuration.getInstance().DB_MAX_CONNECTIONS;
if(availableConnections.size() < MAX_POOL_SIZE)
{
return false;
}
return true;
}
//Creating a connection
private Connection createNewConnectionForPool()
{
Configuration config = Configuration.getInstance();
try {
Class.forName(config.DB_DRIVER);
Connection connection = (Connection) DriverManager.getConnection(
config.DB_URL, config.DB_USER_NAME, config.DB_PASSWORD);
return connection;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public synchronized Connection getConnectionFromPool()
{
Connection connection = null;
if(availableConnections.size() > 0)
{
connection = (Connection) availableConnections.get(0);
availableConnections.remove(0);
}
return connection;
}
public synchronized void returnConnectionToPool(Connection connection)
{
availableConnections.add(connection);
}
}
DataSource.java
package com.jmanne.db;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
public class DataSource {
static JdbcConnectionPool pool = new JdbcConnectionPool();
public static Connection getConnection() throws ClassNotFoundException, SQLException{
Connection connection = pool.getConnectionFromPool();
return connection;
}
public static void returnConnection(Connection connection) {
pool.returnConnectionToPool(connection);
}
}
Just use Semaphores. Ideally what you should do is use CP3O or DBCP as your connection pool. Now you can throttle you connection based on Semaphore.
Every time you do Get, you acquire and on every Release you release it from Semaphore. More over semaphores are thread safe.
Use one of the existing, e.g. Apache DBCP
The connections returned by the pool are often proxies which "ignore" the call to close() from the application. When the connections are returned to the pool, they can be reused. Pools also close and reopen automatically if necessary.
If your application runs on a server, then configure as Data Source, where server will take care of Pooling or else if a simple Java Client then use Apache DBCP(if to the database) or else use Apache Commons Pooling API
See here: Apache Commons
One argument for rolling your own connpool is the configuration and additional jars that are avoided. I agree you need to enable the 3rd party interfaces so you can swap in a mature connpool, but having your own tiny solution can have its place. Self cleaning Vector with synchronized block and a conn wrapper with close() marking the conn as available works very well for servlet apps.
I have one solution for the same to create a connection pool utility which can help you create a pool of default size 10.
#Component
public class ConnectionPool {
private static final Logger logger = LoggerFactory.getLogger(ConnectionPool.class);
private static final int MAX_POOL_SIZE_LIMIT = 10;
private BlockingQueue activeConnectinoQueue = new LinkedBlockingQueue<>();
private BlockingQueue usedConnectinoList = new LinkedBlockingQueue<>();
private int initialPoolSize = 5;
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;
public void initConnectionPool() {
logger.info("ConnectionPool initialization started.");
if(activeConnectinoQueue.isEmpty() && usedConnectinoList.isEmpty()) {
for (int i=0; i<initialPoolSize; i++) {
createConnections();
}
}
logger.info("ConnectionPool initialization completed. ConnectionPool size : {}", activeConnectinoQueue.size());
}
private void createConnections() {
try {
Connection connection = dataSource.getConnection();
activeConnectinoQueue.add(connection);
}catch (SQLException e) {
logger.error("Error in getting connection from pool : ", e);
}
}
public Connection getConnection() {
if(activeConnectinoQueue.isEmpty()) {
initConnectionPool();
}
Connection connection = activeConnectinoQueue.remove();
try {
if(connection.isClosed()) {
connection = dataSource.getConnection();
}
}catch (SQLException e) {
logger.error("Error while getting connection from pool : ", e);
}
usedConnectinoList.add(connection);
return connection;
}
public void releaseConnection(Connection connection) {
if(connection != null) {
usedConnectinoList.remove(connection);
activeConnectinoQueue.add(connection);
}
}
public void setInitialPoolSize(int initialPoolSize) {
if(!(initialPoolSize < 0 || initialPoolSize > MAX_POOL_SIZE_LIMIT)) {
this.initialPoolSize = initialPoolSize;
}
}
public int getInitialPoolSize() {
return initialPoolSize;
}
public int getConnectionPoolSize() {
return activeConnectinoQueue.size() + usedConnectinoList.size();
}
public void setDataSource(AbstractDataSource dataSource) {
this.dataSource = dataSource;
}
public void closeConnectionPool() {
logger.info("Closing connectionPool started.");
close(usedConnectinoList);
close(activeConnectinoQueue);
logger.info("ConnectionPool Closed.");
}
private void close(BlockingQueue<Connection> connectinosQueue) {
for (int i=0; i<connectinosQueue.size(); i++) {
Connection connection = connectinosQueue.remove();
if(connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.error("Error in initializing connection pool : ", e);
}
}
}
}
}
now To make it safe we need to attach a factory object..
public enum ConnectionFactory {
CONNECTION;
private ConnectionPool connectionPool;
public void setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionPool;
}
public Connection getConnection() {
return connectionPool.getConnection();
}
public void closeConnection() {
connectionPool.closeConnectionPool();
}
public void releaseConnection(Connection connection) {
connectionPool.releaseConnection(connection);
}
public int getConnectionPoolSize() {
return connectionPool.getConnectionPoolSize();
}
#Component
public static class ConnectionBuilder {
#Autowired
private ConnectionPool connectionPool;
public void setConnectionPool(ConnectionPool connectionPool) {
this.connectionPool = connectionPool;
}
#PostConstruct
public void postConstruct() {
for (ConnectionFactory cfactory : EnumSet.allOf(ConnectionFactory.class)) {
cfactory.setConnectionPool(connectionPool);
}
}
}
}
Java Connection Pool ?
There are three way to create JDBC Connection Pooling is very easy...
Apache Commons DBCP
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(){ }
}
Now you can get connection
Connection con = DBCPDataSource.getConnection();
HikariCP
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(){}
}
Now you can get connection
Connection con = HikariCPDataSource.getConnection();
C3PO
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(){}
}
Now you can get connection
Connection con = C3poDataSource.getConnection();
I have some model code in Java which has connection pool with multithreading.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
abstract class ObjectPool<T> {
private ConcurrentLinkedQueue<T> pool;
ScheduledExecutorService executorService;
ObjectPool(int minObjects) {
pool = new ConcurrentLinkedQueue<T>();
for (int i = 0; i < minObjects; i++) {
pool.add(createObject());
}
}
ObjectPool(final int minObjects, final int maxSize, final long interval){
pool = new ConcurrentLinkedQueue<T>();
for (int i = 0; i < minObjects; i++) {
pool.add(createObject());
}
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(new Runnable(){
public void run() {
int size = pool.size();
while(size > maxSize){
pool.remove();
}
Iterator i = pool.iterator();
while(i.hasNext()){
T t = (T) i.next();
if(checkExpiry(t)){
System.out.println("Expiry existed...");
i.remove();
}
}
while(pool.size() < minObjects){
System.out.println("Adding more objects to pool");
pool.add(createObject());
}
}
}, interval, interval, TimeUnit.MILLISECONDS);
}
public T borrowObject() {
if (pool.peek() == null)
return createObject();
return pool.remove();
}
public void addObject(T obj) {
if (obj == null)
return;
pool.add(obj);
}
public abstract T createObject();
public abstract boolean checkExpiry(T t);
}
class MultithreadQuery extends Thread{
private ObjectPool<Connection> pool;
private int threadNo;
String query;
MultithreadQuery(ObjectPool<Connection> pool,int threadNo, String query){
this.pool = pool;
this.threadNo = threadNo;
this.query = query;
}
#Override
public void run(){
Connection con = pool.borrowObject();
Statement stmt;
try {
stmt = con.createStatement();
System.out.println("Query started for thread->"+ threadNo);
ResultSet rs=stmt.executeQuery(query);
while(rs.next())
System.out.println(rs.getInt(1)+" "+rs.getString(2)+" "+rs.getString(3));
System.out.println("closing connection....");
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pool.addObject(con);
System.out.println("Query ended for thread->"+ threadNo);
}
}
public class ObjectPoolPatternDemo {
ObjectPool<Connection> pool;
public void setUp(){
pool = new ObjectPool<Connection>(4, 10, 1) {
#Override
public Connection createObject() {
Connection con;
try {
con = DriverManager.getConnection("URL","Username","Password");
return con;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
#Override
public boolean checkExpiry(Connection conn) {
boolean expiryFlag = false;
try {
if(conn.isClosed())
expiryFlag = true;
} catch (SQLException e) {
e.printStackTrace();
}
return expiryFlag;
}
};
}
public static void main(String[] args) throws SQLException {
ObjectPoolPatternDemo oppd = new ObjectPoolPatternDemo();
oppd.setUp();
ExecutorService es = Executors.newFixedThreadPool(4);
String query = "select * from TABLE";
es.execute(new MultithreadQuery(oppd.pool,1,query));
es.execute(new MultithreadQuery(oppd.pool,2,query));
es.execute(new MultithreadQuery(oppd.pool,3,query));
es.execute(new MultithreadQuery(oppd.pool,4,query));
es.execute(new MultithreadQuery(oppd.pool,5,query));
es.execute(new MultithreadQuery(oppd.pool,6,query));
es.shutdown();
try {
es.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("finally completed...");
}
}