I am developing a monitoring plugin that queries multiple database. I would like to use HikariCP in order to keep the connection open, but I do not know how the connection pool has to be instantiated.
Does HikariCP use just one pool for several databases? or just one pool for one database, and it is my responsibility to instantiate as many pools as database I will use.
The latter: a pool is associated to a single database configuration parameters, and it is your responsibility to instantiate as many pools as database I will use. Create the pools accordingly.
I have a DataSourceFactory to accomplish this:
public final class DataSourceFactory {
private static final Logger LOG = LoggerFactory.getLogger(DataSourceFactory.class);
//connection to MySQL
private static DataSource mySQLDataSource;
//connection to PostgreSQL
private static DataSource postgresDataSource;
private DataSourceFactory() { }
//generic method to create the DataSource based on configuration
private static DataSource getDataSource(String configurationProperties) {
Properties conf = new Properties();
try {
conf.load(DataSourceFactory.class.getClassLoader().getResourceAsStream(configurationProperties));
} catch (IOException e) {
LOG.error("Can't locate database configuration", e);
}
HikariConfig config = new HikariConfig(conf);
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource;
}
//retrieve the datasource for MySQL
public static DataSource getMySQLDataSource() {
LOG.debug("Retrieving data source for MySQL");
if (mySQLDataSource == null) {
synchronized(DataSourceFactory.class) {
if (mySQLDataSource == null) {
LOG.debug("Creating data source for MySQL");
mySQLDataSource = getDataSource("mysql-connection.properties");
}
}
}
return mySQLDataSource;
}
//retrieve the datasource for Postgres
public static DataSource getPostgresDataSource() {
LOG.debug("Retrieving data source for Postgres");
if (postgresDataSource == null) {
synchronized(DataSourceFactory.class) {
if (postgresDataSource == null) {
LOG.debug("Creating data source for Postgres");
postgresDataSource = getDataSource("postgres-connection.properties");
}
}
}
return postgresDataSource;
}
}
Here's a file configuration example:
dataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
dataSource.url=jdbc:mysql://theHostName:thePort/nameOfDatabase
dataSource.user=user
dataSource.password=thIsIsN07mYR3alPa$s
dataSource.cachePrepStmts=true
dataSource.prepStmtCacheSize=100
dataSource.prepStmtCacheSqlLimit=2048
dataSource.useServerPrepStmts=true
autoCommit=false
maximumPoolSize=10
Related
I have database configuration in the properties file:
port=8080
host=host-default
host-default is obviously DNS. Below is my configuration class:
#Configuration
#Slf4j
public class DatabaseConfig {
#Value("${port}")
private int port;
#Value("${host}")
private String hostname;
#Bean
public DatabaseTemplate databaseTemplate() {
try {
return new DatabaseTemplate(client());
} catch (Exception e) {
log.error("Ex: " + e.getMessage(), e);
return null;
}
}
#Bean
public Client client() throws UnknownHostException {
TransportAddress address = new InetSocketTransportAddress(InetAddress.getByName(this.hostname), this.port);
client.addTransportAddress(address);
return client;
}
}
So, there is a problem. When the server is running, and in meantime I change DNS the connection with DB will fall dawn. At this moment I cant refresh configuration. I can catch moment when DNS change but I cannot update config. Have you any idea? I tried to destroy DatabaseTemplate singleton but It does not help. Thanks
You will need to create a new bean that wraps the database connection, then update it based on a schedule :
#Component
public class DataSourceManager implements DataSource{
private DataSource dataSource;
#PostConstruct
#Scheduled(fixedRate=1000)
public void reload() {
// init the datasource
}
public DataSource getDataSource(String dbName) {
return dataSource;
}
#Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
.... wrap all the other DataSource methods
}
I'm trying to create a database connection library to be used in all my apps. I want to make sure that this library is fully unit tested and so i'm trying to use dependency injection.
I have this class which i want to ensure is tested:
public class ConnectionFactory {
private String dataSourceName;
public ConnectionFactory(String dataSourceName) {
if(dataSourceName == null) {
throw new NullPointerException("dataSourceName can't be null");
}
this.dataSourceName = dataSourceName;
}
public Connection getConnection() throws SQLException {
Connection connection = getDataSource(dataSourceName).getConnection();
if(connection != null) {
return connection;
}
...
}
// Get a datasource object
private DataSource getDataSource(String dataSourceName) {
...
try {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:comp/env/" + dataSourceName);
} catch (NamingException e) {
...
}
return dataSource;
}
}
I want to be able to simply call this class from all my apps with something as simple as this:
public class MyApp {
public static void main(string[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource");
Connection connection = connectionFactory.getConnection();
}
}
I've started writing unit tests for this ConnectionFactory, but quickly realized that with my current code I can't mock the DataSource object so it's trying to actually connect to a real data source.
#RunWith(Nested.class)
public class ConnectionFactoryTest {
public class Constructor {
#Test
public void shouldThrowNullPointerIfParamIsNull() {
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> { new ConnectionFactory(null); })
.withMessage("dataSourceName can't be null");
}
}
public class GetConnection {
public class WithDataSourceAvailable {
#Test
public void shouldErrorIfParamIsNull() {
ConnectionFactory connectionFactory = new ConnectionFactory("jdbc/myDataSource"); // <-- This is going to fail b/c it's trying to actually fetch a real data source
}
}
}
}
How can I properly use Dependency Injection so that I can write unit tests that don't actually try to connect to a data source?
Take a look at Mockito I find it easy to use for this type of mocking.
I wanted to check if the jdbc code that I am using to get connection is thread-safe:
Environment:
Database - MS SQL Server 2005 Express Edition
Driver Class Name - com.microsoft.sqlserver.jdbc.SQLServerDriver
JDBC Jar: sqljdbc4.jar (downloaded from Microsoft website)
Application Server - Apache Tomcat 7.0
Here are the parameters under the resource element in context.xml file:
name="jdbc/xyzResourceName"
auth="Container"
type="javax.sql.DataSource"
username="someUserName"
password="somePassword"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://someHost;instanceName=someInstance;schema=someSchema;DatabaseName=someDB;"
maxActive="8"
maxIdle="4"
I am using a singleton named HMDataService and using its static methods to do some CRUD operations. Here is the HMDataService class:
public class HMDataService {
private static HMDataService instance = null;
private static **DataSource ds** = null;
protected HMDataService() {
// Exists only to defeat instantiation.
}
public static HMDataService getInstance() {
if (instance == null) {
instance = new HMDataService();
Context ctx;
try {
// Context lookup hardcoded only for simplicity
ctx = new InitialContext();
ctx = (Context) ctx.lookup("java:comp/env");
**ds = (DataSource) ctx.lookup("jdbc/xyzResourceName");**
} catch (NamingException e) {
log.error("Some Problem To Log", e);
}
}
return instance;
}
public static void someInsertOperation(String insertRecord) throws SQLException {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
**connection = ds.getConnection();**
preparedStatement = connection.prepareStatement("Some SQL Here");
preparedStatement.setString(1, insertRecord);
preparedStatement.executeUpdate();
} catch (SQLException e) {
log.error("Some Error Here", e);
} finally {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
}
}
}
This HMDataService and its method someInsertOperation will be called randomly by any number of threads from either a servlet or a jsp.
The sample code to call it is as follows:
HMDataService.getInstance().someInsertOperation("Some Record To Insert");
I want to know if this is thread safe or not ? I've checked online for SQLServerDriver documentation on Microsoft website, but I couldn't get any information whether the datasource.getConnection() is thread safe or not. I know that the thread safety is specific to the particular JDBC driver implementation. But, I don't know if it is for this particular implementation of JDBC driver for MS SQL Server.
I am using JDBC connection pooling in Tomcat. To retrieve connections I have defined a connection factory as below:
public class ConnectionManager {
// reference to the ConnectionManager
private static ConnectionManager instance = null;
// Connection to MySQL database
private Connection connect = null;
private static DataSource ds = null;
// Logger
public static final Logger logger = Logger
.getLogger(ConnectionManager.class);
static {
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Private Constructor .. since its a singleton
*
*/
private ConnectionManager() {
}
public static ConnectionManager getInstance() {
if (instance == null) {
instance = new ConnectionManager();
}
return instance;
}
public Connection getDbConnection() {
Connection conn = null;
try {
synchronized (DataSource.class) {
conn = ds.getConnection();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public void closeDbConnection() throws SQLException {
conn.close();
}
}
Now I see that my code always gets stuck at conn = ds.getConnection(); line. Please let me know what I am doing wrong. From my DAO methods I am using the following to get connection: conn = ds.getConnection();
Clearly its a multi-threading issue. What should I do?
Most of your class seems geared around retrieving the JNDI datasource and using it to create connections. Not necessarily a bad idea but in this case you have introduced some bugs into your program with the additional complexity.
First off, your singleton is not a singleton. Your are not synchronizing the getInstance method so its possible to multiple threads to invoke this method at the same time. The best method in Java (unfortunately) for implementing singletons is via an enum:
public enum ConnectionManager {
INSTANCE;
}
Your second significant issue is that you are synchronizing on a class that you don't explicitly control. There is nothing preventing third party JARs or even other classes in your own application from synchronizing on the DataSource class, making it a rife target for deadlocking issues. I would take out all the superfluous methods from the class and remove the synchronize block:
public enum ConnectionManager {
INSTANCE;
private DataSource ds = null;
ConnectionManager() {
try {
final Context initCtx = new InitialContext();
final Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
if(ds == null) return null;
return ds.getConnection();
}
}
Now, most datasource implementations are thread safe in my experience, so the above code should work most of the time. But, we shouldn't rely on implementations we cannot control, so lets add a safe synchronization to the code, like so:
public enum ConnectionManager {
INSTANCE;
private DataSource ds = null;
private Lock connectionLock = new ReentrantLock();
ConnectionManager() {
try {
final Context initCtx = new InitialContext();
final Context envCtx = (Context) initCtx.lookup("java:comp/env");
ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
} catch (NamingException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
if(ds == null) return null;
Connection conn = null;
connectionLock.lock();
try {
conn = ds.getConnection();
} finally {
connectionLock.unlock();
}
return conn;
}
}
You don't have to add wrapper methods to close the connection, that is the responsibility of the calling code. Good luck.
#arya, seems like you are having the problem of connection leak, and because of that the pool is getting exhausted and the code just waits till it gets a new connection, To analyze the problem , use any of the database monitoring tools, or manually try to trace the leak (The point in code where you have consumed a connnection but forgot to return it to the pool after use).
Well I would say first try out your dataSource is working or not with a test source.
I suggest look at Apache Tomcat JNDI Data Resource How To, for Apache Tomcat 6.0 and for Apache Tomcat 7.0.
Look at the instructions carefully and analyse what's going wrong in your code, then update your question with specific problem.
That code is virtually guaranteed to cause connection leaks in a multithreaded system. closeDbConnection() closes only the last connection borrowed from pool - so if 10 threads have called getDbConnection(), and after that closeDbConnection(), only 1 connection is closed and 9 still alive. Repeat that several times and pool is exhausted (unless connection is cleaned up in finalize(), but that's probably not the case). I would get rid of the whole class, or reworked it to act only as a datasource locator.
I am creating a java application that connects to multiple databases. A user will be able to select the database they want to connect to from a drop down box.
The program then connects to the database by passing the name to a method that creates an initial context so it can talk with an oracle web logic data source.
public class dbMainConnection {
private static dbMainConnection conn = null;
private static java.sql.Connection dbConn = null;
private static javax.sql.DataSource ds = null;
private static Logger log = LoggerUtil.getLogger();
private dbMainConnection(String database) {
try {
Context ctx = new InitialContext();
if (ctx == null) {
log.info("JDNI Problem, cannot get InitialContext");
}
database = "jdbc/" + database;
log.info("This is the database string in DBMainConnection" + database);
ds = (javax.sql.DataSource) ctx.lookup (database);
} catch (Exception ex) {
log.error("eMTSLogin: Error in dbMainConnection while connecting to the database : " + database, ex);
}
}
public Connection getConnection() {
try {
return ds.getConnection();
} catch (Exception ex) {
log.error("Error in main getConnection while connecting to the database : ", ex);
return null;
}
}
public static dbMainConnection getInstance(String database) {
if (dbConn == null) {
conn = new dbMainConnection(database);
}
return conn;
}
public void freeConnection(Connection c) {
try {
c.close();
log.info(c + " is now closed");
} catch (SQLException sqle) {
log.error("Error in main freeConnection : ", sqle);
}
}
}
My problem is what happens if say someone forgets to create the data source for the database but they still add it to the drop down box? Right now what happens is if I try and connect to a database that doesn't have a data source it errors saying it cannot get a connection. Which is what I want but if I connect to a database that does have a data source first, which works, then try and connect to the database that doesn't have a data source, again it errors with
javax.naming.NameNotFoundException: Unable to resolve 'jdbc.peterson'. Resolved 'jdbc'; remaining name 'peterson'.
Which again I would expect but what is confusing me is it then grabs the last good connection which is for a different database and process everything as if nothing happened.
Anyone know why that is? Is weblogic caching the connection or something as a fail safe? Is it a bad idea to create connections this way?
You're storing a unique datasource (and connection, and dbMainConnection) in a static variable of your class. Each time someone asks for a datasource, you replace the previous one by the new one. If an exception occurs while getting a datasource from JNDI, the static datasource stays as it is. You should not store anything in a static variable. Since your dbMainConnection class is constructed with the name of a database, and there are several database names, it makes no sense to make it a singleton.
Just use the following code to access the datasource:
public final class DataSourceUtil {
/**
* Private constructor to prevent unnecessary instantiations
*/
private DataSourceUtil() {
}
public static DataSource getDataSource(String name) {
try {
Context ctx = new InitialContext();
String database = "jdbc/" + name;
return (javax.sql.DataSource) ctx.lookup (database);
}
catch (NamingException e) {
throw new IllegalStateException("Error accessing JNDI and getting the database named " + name);
}
}
}
And let the callers get a connection from the datasource and close it when they have finished using it.
You're catching JNDI exception upon lookup of the nonexistent datasource but your singleton still keeps the reference to previously looked up datasource. As A.B. Cade says, null reference to ds upon exception, or even before that.
On a more general note, perhaps using Singleton is not the best idea.