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.
Related
I'm creating a JavaFX application, I've connected to the database fine. However when i look to get data from the tables i get the error
org.h2.jdbc.JdbcSQLException: Table "LECTURE" not found; SQL
statement: SELECT NAME FROM Lecture [42102-192]
and I'm 100% sure i'm connected to the database and the table is definitely there, any suggestions on why this is?
hear is my connection code and the code i am running just so you can see
public class ConnectionFactory {
//static reference to itself
private static ConnectionFactory instance = new ConnectionFactory();
public static final String URL = "jdbc:h2:file:~/db\\.";
public static final String USER = "notepad";
public static final String PASSWORD = "password";
public static final String DRIVER_CLASS = "org.h2.Driver";
//private constructor
private ConnectionFactory() {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private Connection createConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
System.out.println("ERROR: Unable to Connect to Database.");
}
return connection;
}
public static Connection getConnection() {
return instance.createConnection();
}
}
And the query being run
private void onLoadYearSelect() {
try {
Connection con = ConnectionFactory.getConnection();
Statement stat = con.createStatement();
String query = "SELECT NAME FROM Lecture";
ResultSet years = stat.executeQuery(query);
while(years.next()){
yearSelect.setValue(years.getString("NAME"));
System.out.println(years.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void initialize(){
onLoadYearSelect();
}
If it says the table doesn't exist, then it really doesn't exist.
Most likely, you are not actually connecting to the correct database. In fact, by default, if the connection string points to a non-existent database, it just creates an empty database on the fly, which would explain your error.
It's probably too late now (because there is probably a 2nd database created already somewhere), but to avoid this confusion, it's not a bad idea to include IFEXISTS=TRUE in the connection string so that it fails if the database doesn't exist, rather than creating an empty one that will mask the true problem.
public static final String URL = "jdbc:h2:file:~/db\\.;IFEXISTS=TRUE";
However, one thing you can still try to debug the problem, is to add IFEXISTS=TRUE to the connection string. Then move or rename the database you think it should be connecting to so as to make the connection string invalid. Basically, force it to fail. If the code still connects to the database successfully, then you'll know the connection string is not pointing to the location you think it is.
We have a Web Application which will serve more than 1000 concurrent users
Currently , the Utility class for obtaining the DB Connnection is
public static Connection getDBConnection()
{
Connection conn = null;
try
{
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/MyLocalDB");
try
{
conn = ds.getConnection();
}
catch ( SQLException sqlEx )
{
System.out.println( "cannot get JDBC connection: " + sqlEx );
}
}
catch ( NamingException nEx )
{
nEx.printStackTrace();
}
return conn;
}
Option 2 :
public class DBConnection2 {
private static DataSource dataSource;
static {
try {
dataSource = (DataSource) new InitialContext().lookup("java:/comp/env/jdbc/MyLocalDB");
} catch (NamingException e) {
try {
throw new Exception("'jndifordbconc' not found in JNDI",e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
Please let me know whats the better option (I guess its second operation as lookup is a costly operation and i am doing it only for once in a application .)
please share your views .
You should adopt the second approach. As you say you don't need to keep looking up the datasource in JNDI.
I am assuming that you are using a pooled data source as provided by the tomcat-jdbc project, otherwise performance will be terrible.
On the second approach you should be sure to use connection pooling. Otherwise users will have to wait for others.
For example To Tomcat brings it own connection pool.
Or if you don't use Tomcat maybe you could have a look at C3p0: JDBC DataSources/Resource Pools.
see: Instantiating and Configuring a ComboPooledDataSource
The second option initialize dataSource only once when the class is loaded, then it is shared around all DBConnection2 instances. The first option initialize a new DataSource instance every time you call getDBConnection(). Thus the second option has better performance. But please be pay attention to connection release issue. Make sure close the connection if you choose the first option. I prefer use a framework to handle DB connection, like Spring JDBC: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html
I have many threads accessing MYSQL database, at first I didn't use connection pool so I had this error "You can't operate on a closed ResultSet"
I searched on Google and found out that I should used connection pool so I tried c3p0 API for implementation, but I still have the same problem and nothing changed.
so should I Synchronize getAllcountries method or there's another better solution.
public class DataSource {
private static DataSource datasource;
private ComboPooledDataSource cpds ;
private DataSource() throws IOException, SQLException, PropertyVetoException {
cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver"); //loads the jdbc driver
cpds.setJdbcUrl("jdbc:mysql://localhost/question_game");
cpds.setUser("root");
cpds.setPassword("");
cpds.setMaxPoolSize(500);
}
public static DataSource getInstance() throws IOException, SQLException, PropertyVetoException {
if (datasource == null) {
datasource = new DataSource();
return datasource;
} else {
return datasource;
}
}
public Connection getConnection() throws SQLException {
return this.cpds.getConnection();
}
public List<Country> getAllCountries() {
String query = "SELECT * FROM country order by name ";
List<Country> list = new ArrayList<Country>();
Country country = null;
ResultSet rs = null;
try {
try {
connection = DataSource.getInstance().getConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (PropertyVetoException e) {
e.printStackTrace();
}
statement = connection.createStatement();
rs = statement.executeQuery(query);
while (rs.next()) {
//getting countries
}
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
//DbUtil used to check if null
DbUtil.close(rs);
DbUtil.close(statement);
DbUtil.close(connection);
}
return list;
}
In addition to #stephen-c 's observation,
you basically have two options: either synchronize getAllCountries method, or make the database connection local to that method instead of having it as a class member.
As you have it now, 'connection' is a class member (available to all invocations of getAllCountries(), on all threads), so the connection is probably being overwritten by a second thread. Move it to a variable in the method, and then each invocation of the method will have its own connection.
I see a method called getConnection, I don't see where you are calling it. Instead, I see your getAllCountries method using a static DataSource object that is created like this:
new DataSource();
I searched on Google and found out that I should used connection pool so I tried c3p0 API for implementation, but I still have the same problem and nothing change
Yea ...
It looks like you just cut-and-pasted the code you found into your application without really thinking about it. Obviously ... you need to understand what the code is doing AND figure out how to use it in your application.
As you can see, this code is part of my Data Access Object Layer.
I've never used the ConnectionPool Object before because i'm still studying Java.
Anyway, i'm getting an error message stating :
The method getInstance() is undefined for the type ConnectionPool.
(at line 5)
Should any of you have experienced this before, help would be appreciated.
import java.sql.*;
import java.util.*;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import music.business.*;
public class ProductDB
{
//This method returns null if a product isn't found.
public static Product selectProduct(String productCode)
{
ConnectionPool pool = ConnectionPool.getInstance(); //<===<====<====<=================
Connection connection = pool.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
String query = "SELECT * FROM Product " +
"WHERE ProductCode = ?";
try
{
ps = connection.prepareStatement(query);
ps.setString(1, productCode);
rs = ps.executeQuery();
if (rs.next())
{
Product p = new Product();
p.setCode(rs.getString("ProductCode"));
p.setDescription(rs.getString("ProductDescription"));
p.setPrice(rs.getDouble("ProductPrice"));
return p;
}
else
{
return null;
}
}
catch(SQLException e)
{
e.printStackTrace();
return null;
}
finally
{
DBUtil.closeResultSet(rs);
DBUtil.closePreparedStatement(ps);
pool.freeConnection(connection);
}
}
I just found out that i made a mistake:
- the ConnectionPool in my, above mentioned, class was not supposed to be Tomcat imported. Its a JNDI class. See below. The getInstance is actually a method in my JNDI class. Sorry for waisting your time guys. Thank you
import java.sql.*;
import javax.sql.DataSource;
import javax.naming.InitialContext;
public class ConnectionPool
{
private static ConnectionPool pool = null;
private static DataSource dataSource = null;
public synchronized static ConnectionPool getInstance()
{
if (pool == null)
{
pool = new ConnectionPool();
}
return pool;
}
private ConnectionPool()
{
try
{
InitialContext ic = new InitialContext();
dataSource = (DataSource) ic.lookup("java:/comp/env/jdbc/musicDB");
}
catch(Exception e)
{
e.printStackTrace();
}
}
public Connection getConnection()
{
try
{
return dataSource.getConnection();
}
catch (SQLException sqle)
{
sqle.printStackTrace();
return null;
}
}
public void freeConnection(Connection c)
{
try
{
c.close();
}
catch (SQLException sqle)
{
sqle.printStackTrace();
}
}
}
A ConnectionPool can be created using its constructor which accepts some pool properties. Even though this constructor is exposed creating a pool within your application, especially in each DAO may not be advisable.
The point of pooling is to have a single pool of connections that the application can retrieve a connection from when it needs to perform some work with the database. Using this design the code will have multiple connection pools, which defeats the point of pooling.
Usually a datasource is established within Tomcat, which internally handles the building of a connection pool. See these resources for more about connection pooling in Tomcat.
Create it in a 'DBService' class & share it, if you really don't want to define it in Tomcat. As Duffy says, defining it in Tomcat probably would be the best way..
For defining it in Tomcat: http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html#Database_Connection_Pool_(DBCP)_Configurations
The getInstance() syntax appears all wrong. For creating & configuring it directly, see: http://people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html
I would have Tomcat manage that pool for you. It has instructions on how to create a JNDI data source. You should do that and get this out of your code.
It'll have the added benefit of externalizing the connection parameters from your app. They'll live in configuration on the app 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.