I am creating a java application using SwingGUI. I have used singleton classes for frames (to avoid creation of multiple frames). Also, the class uses database connectivity. Now, at the start of the application (HomeScreen), I have created and initialised the DB connection and close it when the app is closed. However, the DB statement is created privately in classes (the class is like a form to entry info) everytime I need to access the DB. After I use the statement for DB manipulation, I close it as well as the form. Now when I reopen the form (without restarting the app), and try to enter values in the form, I am shown the error:
Error executing insert query !!!
No operations allowed after statement closed.
which is kind of obvious because the form frame is a singleton class and once the connection is closed it is not re-established (the constructor is not called).
the HomeScreen (home page of the app)
public class HomeScreen extends javax.swing.JFrame {
private static HomeScreen _instance;
/**
* Creates new form HomeScreen
*/
private HomeScreen() {
initComponents();
showMessageOnAppClose();
dbc = new DBConnection();
dbc.init();
dbConn = dbc.getMyConnection();
}
public static HomeScreen getInstance(){
if (_instance == null){
_instance = new HomeScreen();
}
return _instance;
}
the form:
public class StudentMemberRegist extends javax.swing.JFrame {
private static StudentMemberRegist _instance;
/**
* Creates new form StudentMemberRegist
*/
private StudentMemberRegist() {
initComponents();
Util.showMessageOnWindowClose(this);
try {
stmt = HomeScreen.dbConn.createStatement();
} catch (SQLException e) {
}
setFormValues();
}
public static StudentMemberRegist getInstance() {
if (_instance == null) {
_instance = new StudentMemberRegist();
}
return _instance;
}
the DBConnection class
public class DBConnection {
private Connection dbConnection;
/** Creates new instance of DBConnection */
public DBConnection(){
}
public void init(){
try{
Class.forName("com.mysql.jdbc.Driver");
dbConnection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/LibManagement", "root", "ace");
}
catch(Exception e){
System.out.println("Failed to get connection <class:DBConnection>");
e.printStackTrace();
}
}
public Connection getMyConnection() {
return dbConnection;
}
public void close(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
}
}
}
public void destroy() {
if (dbConnection != null) {
try {
dbConnection.close();
} catch (Exception e) {
}
}
}
Now, I am faced with the dilemma of never closing the database statement (stmt), or creating a public static Statement, using it throughout, and closing it when the application exits. Not closing the statement is probably not an option. Also, using a static statement, will be difficult to manage throughout. Help me out here!
You shouldn't use a DB connection as an attribute in a singleton class. It's bad design. By definition, your singleton which is a GUI element shouldn't even be concerned about DB connections at all.
What you should do is have a CRUDManager class (for database create/read/update/delete) that can be called by your singleton. This would have 4 publics methods, as CRUD suggests. And it should handle it's own connections when needed (and close them when a transaction is finished, don't leave a connection open at application scope !). This should be transparent to the GUI.
Related
I am learning about MySQL database and I cannot quite understand one concept. Lets say there are two methods in the same class as the one shown below. Now, do i have to use Connection connect = dbConnection.getDBConnection(); in each method or is there a different way to declare one connection and use it across multiple methods?:
private void setUpdateButton(ActionEvent event) {
try{
Connection connect = dbConnection.getDBConnection();
Statement stmt = connect.createStatement();
if(txtID.getText().trim().isEmpty()||txtFirstName.getText().trim().isEmpty() || txtSecondName.getText().trim().isEmpty() ||
txtGender.getText().trim().isEmpty() || txtAge.getText().trim().isEmpty() || txtHomeAddress.getText().trim().isEmpty() || txtPhoneNumber.getText().trim().isEmpty()) {
showAlert("Invalid Input!", "All fields need to be populated before updating.");
}else {
String sqlQuery ="update student_information set Age ='"+Integer.parseInt(txtAge.getText())+"',Name ='"+txtFirstName.getText()+"',Surename='"+txtSecondName.getText()
+"',Gender='"+txtGender.getText()+"',Address='"+txtHomeAddress.getText()+"',PhoneNumber='"+txtPhoneNumber.getText()+"'where ID="+Integer.parseInt(txtID.getText());
stmt.executeLargeUpdate(sqlQuery);
setTxtArea();
showConfAlert("Update Completed!", "Record has been updated!");
Creating connections is a costly operation, so I think you should open the connection at the application startup, and close it on exit.
If your program is not multi thread you would be fine with a simple global object, otherwise other strategies should be used.
You can create a singleton for your app with a method returning the connection.
public class App {
private static App self;
private Connection connection;
private App(){
}
public synchronized App getInstance(){
if(self == null){
self = new App();
}
return self;
}
public synchronized Connection getConnection()throws SQLException {
if(connection==null || !isValid(connection)){
// Create a new connection
}
return connection;
}
private boolean isValid(Connection conn) {
// Check if the connection is valid otherwise return false
return false;
}
public static synchronized void close(){
try{
self.connection.close();
} catch (SQLException e){
// Swallow exception
} finally {
self = null;
}
}
}
You can get the connection anywhere like this:
Connection conn = App.getInstance().getConnection();
You should make sure to close the connection on exit, maybe with a shutdown hook.
You could also, create a wrapper around your connection that forwards all connection methods to the original connection , with the exception of close that marks the connection available , this way you create something like 1 connection pool.
If the connection is available you return it otherwise you either wait or throw or do what is most appropriate for your application.
I have a db design issue that I am facing with one of my projects. I am trying to implement a service and part of that service is a db layer. It is setup such that I have helper classes that perform get/update methods to the database and a layer on top of them that is a janitor. For ex:
public class GetStudentDBHelper {
public List<Student> get(List<Integer> ids) {
Conn getConnection...
// run sql query and construct returning Student objects
}
public List<Student> get(List<Classroom> byClassroom) {
// get all students in passed in classrooms
// run sql query and construct returning Student objects
}
}
public class StudentJanitor {
public GetStudentDBHelper getStudentDBHelper;
public UpdateStudentDBHelper updateStudentDBHelper;
public UpdateClassroomDBHelper updateClassroomDBHelper;
public List<Student> getStudents(List<Integer> ids) {
return getStudentDBHelper.get(ids);
}
public void saveStudents(List<Students> students, int classRoomid) {
Connection conn = Pool.getConnection(); // assume this gives a jdbc
conn.autocommit(false);
try {
try
{
updateStudentDBHelper.saveForClassroom(students, classRoomid, conn);
updateClassroomDBHelper.markUpdated(classRoomid, conn);
conn.commit();
}
catch
{
throw new MyCustomException(ErrorCode.Student);
}
}
catch (SQLException c)
{
conn.rollback();
}
finally {
conn.close();
}
}
public class ClassroomJanitor{
public void saveClassRoon(List<Classrooms> classrooms) {
Connection conn = Pool.getConnection()// assume this gives a jdbc
conn.autocommit(false);
try {
try {
updateClassroomDBHelper.save(classrooms, conn);
updateStudentDBHelper.save(classrooms.stream().map(Classroom::getStudents).collect(Collections.toList()), conn);
conn.commit();
}
catch {
throw new MyCustomException(ErrorCode.ClassRoom);
}
}
catch (SQLException c)
{
conn.rollback();
}
finally {
conn.close();
}
}...
public class GetClassroomDBHelper{}...
public class UpdateClassroomDBHelper{}...
The update db classes all compose multiple other updators in case they need to update values in other tables (ie. saving a student means I have to touch a classroom table in which a student belongs to update its last updated time for instance).
The issue I am having is for the update db classes, I have to pass in a connection from my Janitor class if i am touching multiple tables in order to have transactions and their rollback capabilities. See above for what I mean. Is there a better way to do this? This type of try, catch, pass in conn to db helpers, will have to be done for any multi transaction operation in my janitors.
In short, you can see that the code is generally like this duplicated across multiple methods:
Connection conn = Pool.getConnection()// assume this gives a jdbc
conn.autocommit(false);
try {
try {
//do some business logic requiring Connection conn
}
catch {
throw new MyCustomException(ErrorCode);
}
}
catch (SQLException c)
{
conn.rollback();
}
finally {
conn.close();
}
Whenever you have a code sequence that is duplicated but it only differs in some parts you can use a template method.
In your case I would introduce a TransactionTemplate class and use a callback interface for the parts that are different. E.g.
public class TransactionTemplate {
private DataSource dataSource;
public TransactionTemplate(DataSource dataSource) {
this.dataSource = Objects.requireNonNull(dataSource);
}
public <T> T execute(TransactionCallback<T> transactionCallback) throws Exception {
Connection conn = dataSource.getConnection();// assume this gives a jdbc
try {
conn.setAutoCommit(false);
T result = transactionCallback.doInTransaction(conn);
conn.commit();
return result;
} catch (Exception e) {
conn.rollback();
throw e;
} finally {
conn.close();
}
}
}
The callback interface would look like this
public interface TransactionCallback<T> {
public T doInTransaction(Connection conn) throws Exception;
}
As you can see the TransactionTemplate manages the transaction while the TransactionCallback implements the logic that must be done in one transaction.
Your client code will then look like this
public class StudentJanitor {
private TransactionTemplate transactionTemplate;
StudentJanitor(DataSource dataSource) {
transactionTemplate = new TransactionTemplate(dataSource);
}
public void saveStudents(List<Students> students, int classRoomid) {
SaveStudentsTransaction saveStudentsTransaction = new SaveStudentsTransaction(students, classRoomid);
transactionTemplate.execute(saveStudentsTransaction);
}
}
and the logic is placed in the TransactionCallback
public class SaveStudentsTransaction implements TransactionCallback<Void> {
public GetStudentDBHelper getStudentDBHelper;
public UpdateStudentDBHelper updateStudentDBHelper;
public UpdateClassroomDBHelper updateClassroomDBHelper;
private List<Students> students;
private int classRoomid;
public SaveStudentsTransaction(List<Students> students, int classRoomid) {
this.students = students;
this.classRoomid = classRoomid;
}
#Override
public Void doInTransaction(Connection conn) throws Exception {
try
{
updateStudentDBHelper.saveForClassroom(students, classRoomid, conn);
updateClassroomDBHelper.markUpdated(classRoomid, conn);
conn.commit();
}
catch
{
throw new MyCustomException(ErrorCode.Student);
}
return null;
}
}
Two main concerns you are currently facing are the boiler plate code for repetitive tasks related to connection (get/execute/close etc)
and infrastructure for getting the same connection across method boundaries. The first is typically solved using Template pattern and the latter
using Threadlocal variables to pass around appropriate connection across methods. These type of concerns have been solved in Java world long ago but
will require you to rely on framework like Spring (JDBC template) etc which have this feature from last decade or so or you would need to roll out stripped
down version of this infrastructure. If you are interested in latter then you can take hint from similar attmepts shared on Github like this.
How to get same connection from a DataSource in multiple classes? Is it possible?
Suppose I have a DataSource which I pass to 2 classes. The DataSource is using a Connection Pooling.
Then I call a method in 1st Class which get a Connection from the DataSource, uses that Connection and then closes that Connection.
Then I call a method in 2nd Class which get a Connection from the DataSource, uses that Connection and then closes that Connection.
Is it possible to be sure that the method in 2nd Class will get the same Connection which was used by method in 1st Class?
This is the example code:
This is the 1st class whose method will be called by the unit of work:
public class Class1 {
private DataSource dataSource = null;
public Class1(DataSource dataSource) {
this.dataSource = dataSource;
}
public void class1Method1() throws Exception {
Connection conn = null;
try {
conn = dataSource.getConnection();
... // do your work
} finally {
try {
if (conn != null)
conn.close();
} catch (Exception e) {
} finally {
conn = null;
}
}
}
}
This is the 2nd class whose method will be called by the unit of work:
public class Class2 {
private DataSource dataSource = null;
public Class2(DataSource dataSource) {
this.dataSource = dataSource;
}
public void class2Method1() throws Exception {
Connection conn = null;
try {
conn = dataSource.getConnection();
... // do your work
} finally {
try {
if (conn != null)
conn.close();
} catch (Exception e) {
} finally {
conn = null;
}
}
}
}
And this is my unit of work:
InitialContext initialContext = null;
DataSource dataSource = null;
Class1 class1 = null;
Class2 class2 = null;
initialContext = new InitialContext();
dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/MyDB");
class1 = new Class1(dataSource);
class2 = new Class2(dataSource);
class1.class1Method1();
class2.class2Method1();
Closing a connection releases the connection resources, see here and connections should always be closed. Depending on the DataSource implementation (e.g. connection pool) it could then for instance give that same connection back on the next getConnection call. But it does not have to do so, it can also terminate the first connection and return a new one each time.
For more info, see here and here.
As #EJP said in a comment, You would need to pass the Connection around into the other class before you close it. You'd also need to make sure the second class doesn't close it if you plan to use it later in the outer class. This is a low level solution and quite a headache.
You can use Spring or Java EE to solve this problem at a higher level. It lets you mark all your relevant methods to say, "these should be transactional". If you call one for the first time, a new transaction is started. When you leave that outer method, the transaction automatically ends. But, if that method calls another transactional method, it knows the transaction is still open so it reuses it. It removes a lot of headache.
I believe these two technologies use Aspect Oriented Programming under the hood.
Read more for JEE: http://docs.oracle.com/javaee/6/tutorial/doc/bncih.html
Read more for Spring: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html
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 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.