I made the DAO to create a sales offer and it is similar to an other DAO where it makes users and that connects and works. But the one below keeps skipping the if statements and I'm not sure why it isn't adding to the data base. I ran the same command in the SQL string in oracle and it worked there.
public boolean sendOffer(Sales sell) {
boolean done = false;
int key =0;
Connection conn = cu.getConnection();
try {
String sql = "INSERT INTO sales (offer_amount, offer_who, car_id) values(?,?,?)";
String[]keys= {"ID"};
PreparedStatement ps = conn.prepareStatement(sql,keys);
ps.setInt(1, sell.getOfferAmount());
ps.setInt(2, sell.getOwnerID()); //foriegn key
ps.setInt(3, sell.getCarID()); // forgien key
int number = ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if(number!=1)
{
log.warn("data insert fail");
}
else
{
log.trace("success");
done=true;
}
if(rs.next()) {
key=rs.getInt(1);
sell.setID(key);
conn.commit();
}
else {
log.warn("data not found");
}
}catch(SQLException e)
{
}
finally {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
// TODO Auto-generated method stub
return done;
}'''
The main issue is that an exception is happening in your code but the try/catch block is intercepting it and swallowing it silently. While it may be tempting to catch and swallow exceptions, truth is it always causes more problems than it solves and the key concept of handling exceptions is to NOT handle them: just put the throws declaration and let the application crash.
You then have other possible side-issues depending on how the Connection was obtained in the first place, like the fact that you're never closing the PreparedStatement and the ResultSet (if the connection is closed, they are closed as well... but if the connection is returned to a pool then they are never going to be closed).
In general the above code tends to pack too much functionality in a single method and can quickly spiral out of control, so you might want to divide it in smaller chunks with clear individual responsibilities.
All of the above is common to observe wherever Connection and PreparedStatement are used directly, be it for maximum performance reasons or for lack of experience. Typically in web applications using the Spring framework this is solved through the use of a JdbcTemplate but I cannot assume that you are using Spring so I won't show its usage here.
At a minimum, I would modify your code roughly as follows:
public boolean sendOffer(Sales sell) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = cu.getConnection();
ps = prepareInsertOfferStatement(sell, conn);
ps.executeUpdate();
rs = ps.getGeneratedKeys();
sell.setID(extractKey(rs));
conn.commit();
log.trace("success");
return true;
}
catch(Exception ex) {
log.error(ex); // this is actually probably bad. Consider putting a throws declaration and get rid of this catch
return false;
}
finally {
closeQuietly(rs, ps, conn);
}
}
private void closeQuietly(AutoCloseable... objs) {
for(AutoCloseable obj : objs) {
try {
obj.close();
} catch (SQLException e) {
// this is usually mostly safe to ignore. Maybe log a warning somewhere
}
}
}
private PreparedStatement prepareInsertOfferStatement(Sales sell, Connection conn) throws SQLException {
String sql = "INSERT INTO sales (offer_amount, offer_who, car_id) values(?,?,?)";
String[] keys= {"ID"};
PreparedStatement ps = conn.prepareStatement(sql,keys);
ps.setInt(1, sell.getOfferAmount());
ps.setInt(2, sell.getOwnerID()); //foreign key
ps.setInt(3, sell.getCarID()); // foreign key
return ps;
}
private int extractKey(ResultSet rs) throws SQLException {
if(rs.next()) {
return rs.getInt(1);
}
else {
throw new Exception("The statement did not return any generated key.");
}
}
As you can see it's not shorter, but responsibilities are clearer and all objects are closed accordingly. Furthermore, it gives you nice reusable primitives to close connections and related objects and to extract the key from other inserts you will want to do. Further abstractions would allow you to obtain more primitives, but I think this is sufficient for you to get the gist of it.
Related
I want to wrap some JDBC code into my own and different classes. So the resource is created in those classes, the result set is returned. If the client is ready, then all in that chain opened resources should be closed.
I wrote the following, but I'm not sure if that is correct enough and the easiest way.
I build a SQL string for insertion of a data-row.
From my data source (hikari pool), I get a database connection. Then I create a prepared statement, I execute this and get a ResultSet back.
This result set I return to the caller.
The connection and prepared statement have to remain open, until the caller is ready with that returned result set
public DbResult insert(int cache, String shema, String table, LinkedHashMap<String, Object> data, boolean returnAutoCreate) throws SQLException
{
//building sql-string or take it from my cache
//...
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try
{
connection = ds.getConnection();
preparedStatement = connection.prepareStatement(sqlString);
int i = 0;
for (Object value : data.values())
{
preparedStatement.setObject(i++, value);
}
preparedStatement.executeUpdate();
resultSet = preparedStatement.getResultSet();
DbResult dbResult = new DbResult(connection, preparedStatement, resultSet);
return dbResult;
}
catch (Exception e)
{
try
{
if (resultSet != null) resultSet.close();
}
catch (Exception e2)
{
e.addSuppressed(e2);
}
try
{
if (preparedStatement != null) preparedStatement.close();
}
catch (Exception e2)
{
e.addSuppressed(e2);
}
try
{
if (connection != null) connection.close();
}
catch (Exception e2)
{
e.addSuppressed(e2);
}
throw e;
}
}
Here is my DbResult class
class DbResult implements AutoCloseable
{
private final ResultSet rs;
private final PreparedStatement ps;
private final Connection conn;
public DbResult(Connection conn, PreparedStatement ps, ResultSet rs)
{
this.conn = conn;
this.ps = ps;
this.rs = rs;
}
#Override
public void close() throws SQLException
{
try (conn; ps; rs;){}
}
public ResultSet getResultSet()
{
return rs;
}
}
And here the client/caller-code
DatabaseHelper databaseHelper = new DatabaseHelper(ds, defaultShema);
try(DbResult result = databaseHelper.insert(1001, defaultShema, "test", new LinkedHashMap<>(), true);)
{
//do sth with result
}
In my insert method I added that try catch, cause if something fails there, I must close the resources.
If there is no problem, I add the resources to my DbResult object, and there I close the resources with the close()-method. In that, I use a try-with, cause if in that chain one close fails, then the others are still closed, and the exception is forwarded correctly.
At my caller, I use also a try-with, which calls that close()-method in DbResult after I'm done with the result.
But it's a lot of code, especially the try-catch construct in my insert-method.
I'm not sure, if there is a easier or a better way of doing that.
One more question I have:
I could cache the result, so I don't have to take care of that resource closing outside of my DatabaseHelper class.
There exists a CachedRowSet. But I'm not sure about, how expensive this is. Does that copy also the MetaInformations?
I guess the normal ResultSet retrieves the data first, if I access them:
the meta data, the row-data: row by row?
Is that a good idea to cache the data into ArrayList of Hashmaps?
Arraylist -> Rows
HashMap -> coloums by name -> value
So if I do that, I can close all resources immediately.
-> I don't have to care about that closing outside of my DatabaseClass
-> the pool gets the connection faster back
However, maybe I retrieve data, which I don't need (for example, the meta data).
Also, it's expensive to build a copy of my data into a ArrayList->Hashmap construct.
So what ideas do you have of that; whats the best practice?
I am building a basic java application to load some files into a mysql database. I am able to load the files up and populate my tables without any problems. However after speaking to someone who reviewed my code, I am apparently not correctly closing my connections and wasting resources. Where am I not closing up the connections? Have I done this incorrectly?
I am using the try-with-resources construct within my DbSinger class to execute prepared statements to my database, which should automatically close the connection so long as the AutoCloseable interface is implemented, which it is in the parent class of Db. The close() method however is never reached. The DbSinger is instantiated inside my main() and then runs it's single method populateSingers() with an ArrayList of Singer objects.
Connection Class
public class SQLConnection {
private static final String servername = "localhost";
private static final int port = 3306;
private static final String user = "ng_user";
private static final String pass = "ng";
private static final String db = "ng_music";
private static final String connectionString = "jdbc:mysql://" + servername + ":" + port + "/" + db;
public Connection provide() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(connectionString, user, pass);
}
catch (SQLException | ClassNotFoundException e) {
throw new SQLConnectionException(e);
}
}
public class SQLConnectionException extends RuntimeException {
SQLConnectionException(Exception e) {super(e);}
}
}
Abstract parent class
public abstract class Db implements AutoCloseable{
private Connection connection;
Db() {
SQLConnection sqlC = new SQLConnection();
this.connection = sqlC.provide();
}
#Override
public synchronized void close() throws SQLException {
if(connection != null) {
connection.close();
connection = null;
System.out.println("Connection closed");
}
}
Connection getConnection() {
return connection;
}
boolean checkIfPopulated(String query){
try {
PreparedStatement ps = getConnection().prepareStatement(query);
ResultSet rs = ps.executeQuery();
return !rs.next();
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
}
Concrete class to execute queries to database for singers table
public class DbSinger extends Db {
public DbSinger() {
super();
}
public void populateSingers(ArrayList<Singer> singers) {
String populateSingersQuery = "insert into ng_singers(name, dob, sex) values(?,?,?)";
if(!checkIfPopulated("select * from ng_singers")){
System.out.println("Singer Table is already populated");
return;
}
try (PreparedStatement ps = getConnection().prepareStatement(populateSingersQuery)) {
for (Singer s : singers) {
ps.setString(1, s.getName());
ps.setDate(2, java.sql.Date.valueOf(s.getDob()));
ps.setString(3, s.getSex());
ps.addBatch();
}
ps.executeBatch();
System.out.println("Singers added to table");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
My code is able to execute is able to run fine and does what it needs to, but I want to understand why and where I am not closing connections, and to understand how I can resolve this. Or at least understand if I am approaching this wrong.
In your case, you need to instantiate DBSinger class in try-with-resources statement to close the underlying connection.
Instead of doing:
DbSinger dbSinger = new DbSinger();
You need to do:
try (DbSinger dbSinger = new DbSinger()) {
// Your other code
}
This way the close() method you are overriding in your Db class will be called automatically.
Also, close the preparedStatement you created in your checkIfPopulated method by:
try (PreparedStatement ps = getConnection().prepareStatement(query)) {
// Other codes
}
Your code is old way. And you do need close manually. However, with Java 8, you can use try with resource like below,
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement()) {
try {
stmt.execute(dropsql);
} catch (Exception ignore) {} // ignore if table not dropped
stmt.execute(createsql);
stmt.execute(insertsql);
try (ResultSet rs = stmt.executeQuery(selectsql)) {
rs.next();
} catch (Exception e2) {
e2.printStackTrace();
return("failed");
}
} catch(Exception e) {
e.printStackTrace();
return("failed");
}
I have got a program this way:
public void MethodOne()
{
String sqlquery = "select * from vendor_items where category_id = 1 ";
PreparedStatement consildatedPst = connection.prepareStatement(sqlquery);
ResultSet consilatedReslset = consildatedpst.executeQuery();
while(consilatedReslset.next())
{
String name = consilatedReslset.getString("name");
if(name!=null)
{
MethodTwo();
}
}
}
public void MethodTwo(String name)
{
String sqlquery2 = "select ename from Vendor where name=?";
PreparedStatement otherPst = connection.prepareStatement(sqlquery2);
otherPst.setString(1,name);
}
This is the way connection is established (Later I will go for Connection Pooling).
public class DBConnection {
public static Connection getDBConnection() {
String sURL="jdbc:mysql://localhost:3306/oms";
String sUserName="root";
String sPwd="";
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(sURL, sUserName,sPwd);
return conn;
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return conn;
}
}
My question is Can I use the same connection object when calling within Methods??
Yes, you can.
When you do:
connection.prepareStatement(sqlquery2);
It creates a new statement object using the same connection. So the ResultSets that you obtain from them will belong to different Statements and will be different and there will be NO PROBLEM for you.
In short: Different Statements manage different ResultSets. If you get 2 ResultSets from the same Statement when you get the second one the first one will be dropped but if you have 2 Statements you can manage 2 ResulSets without problem (while the connection is open, of course)
Only if you aren't using the connection in multiple threads or nesting your own methods. In other words, no. Use a new connection per method. To avoid overhead use a connection pool.
I am getting result set is already closed where i am passing resultset into other method.where should i closed result set.
public void mainTest(){
ResultSet rs= pstmt.executeQuery(query);
List list = populateRS(rs);
if(rs!=null)rs.close();
}
public List populateRS(ResultSet rs){
//work with result set
if(rs!=null)rs.close();
}
You should probably use a try-finally block to close the ResultSet even if populateRS (or something else) throws an exception:
ResultSet rs;
try {
rs = pstmt.executeQuery(query);
List list = populateRS(rs);
} finally {
if (rs != null) {
rs.close();
}
}
Close in the same method you open in if at all possible. Consistently doing this makes it easy for code-reviewers and maintainers to easily triage resources into (obviously freed, obviously problematic, and needs more attention).
A few other notes:
Use try (...) or do the closing in finally so the resource is closed even when the code using it fails with an exception.
Use the #WillClose and #WillNotClose annotations as appropriate so that IDEs and tools like findbugs can point out problems.
public void mainTest(){
List<?> list;
try (ResultSet rs = pstmt.executeQuery(query)) {
list = populateRS(rs);
}
// work with list
}
public List<?> populateRS(#WillNotClose ResultSet rs){
//work with result set
}
or if you're stuck with older Java:
public void mainTest(){
List<?> list;
ResultSet rs = pstmt.executeQuery(query);
try {
list = populateRS(rs);
} finally {
if(rs!=null)rs.close();
}
// work with list
}
Use the new try-with-resources statement which would automatically close the ResultSet whether an exception occurs or not because it implements AutoCloseable.
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement.
public void mainTest()
{
try (ResultSet rs = pstmt.executeQuery(query)) {
List list = populateRS(rs);
} catch (SQLException ex) {
}
}
public List populateRS(ResultSet rs){
// work with result set
}
It is good to close where you are opening .
It is good programming practise to close all resouces in finally block
public void mainTest()
{
ResultSet rs = null;
try{
rs= pstmt.executeQuery(query);
List list = populateRS(rs);
}finally{
try {
rs.close();
} catch (SQLException ex) {
}
}
}
public List populateRS(ResultSet rs){
//work with result set
}
according to java docs
Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.
Close things near where you open them. In this case that would be in the mainTest method after you call populateRS. If a method doesn't open something, it shouldn't close it.
You should close the ResultSet in the mainTest method to segregate populateRS method's participation in the lifecycle of the ResultSet.
You should have a finally block in which you should close the ResultSet. This practice gives you a guarantee that the ResultSet is closed even if an exception is thrown.
I'm new to Java and even newer to java database connections. I've managed to create a database connection and query a table when I put it in the Main class. Now that I've moved it into a new class called Connection I am getting errors:
package lokate;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
public class Connection {
private static Statement stmt = null;
private static ResultSet rs = null;
private static Connection con = null;
public Connection() throws SQLException {
try {
Class.forName("com.mysql.jdbc.Driver");
String connectionUrl = "jdbc:mysql://localhost:3306/Lokate?" +
"user=root&password=";
con = DriverManager.getConnection(connectionUrl);
stmt = con.createStatement();
retriveData("SELECT * FROM Users");
int rowsEffected = 0;
} catch (SQLException sqlEx) {
System.out.println("SQL Exception: "+ sqlEx.toString());
} catch (ClassNotFoundException classEx) {
System.out.println("Class Not Found Exception: "+ classEx.toString());
} catch (Exception Ex) {
System.out.println("Exception: "+ Ex.toString());
}
}
public static void retriveData(String SQL) throws Exception {
rs = stmt.executeQuery(SQL);
while (rs.next())
{
System.out.println(rs.getString("fname") + " : " + rs.getString("lname"));
}
}
}
I'm getting an error saying cannot find symbol. Symbol:method createStatement() and incomparable types for con = DriveManager.....
Can anyone help?
Also, is it best practice to put the connection in the class like this then call a new object every time I want to do something with the db?
Regards,
Billy
I'd say your code is an example of many worst practices. Let me count the ways:
Your Connection class is a poor abstraction that offers nothing over and above that of java.sql.Connection.
If you use your class, you'll never get to take advantage of connection pooling.
You hard wire your driver class, your connection URL, etc. You can't change it without editing and recompiling. A better solution would be to externalize such things.
Printing an error message in the catch blocks is far less information than supplying the entire stack trace.
Your code hurts my eyes. It doesn't follow the Sun Java coding standards.
Your retrieveData method is utterly worthless. What will you do with all those printed statements? Wouldn't it be better to load them into a data structure or object so the rest of your code might use that information?
It's rowsAffected - "affect" is the verb, "effect" is the noun. Another variable that's not doing any good.
You're on the wrong track. Rethink it.
I think you'll find this code more helpful.
package persistence;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DatabaseUtils
{
public static Connection createConnection(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException
{
Class.forName(driver);
if ((username == null) || (password == null) || (username.trim().length() == 0) || (password.trim().length() == 0))
{
return DriverManager.getConnection(url);
}
else
{
return DriverManager.getConnection(url, username, password);
}
}
public static void close(Connection connection)
{
try
{
if (connection != null)
{
connection.close();
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public static void close(Statement st)
{
try
{
if (st != null)
{
st.close();
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public static void close(ResultSet rs)
{
try
{
if (rs != null)
{
rs.close();
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public static void rollback(Connection connection)
{
try
{
if (connection != null)
{
connection.rollback();
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public static List<Map<String, Object>> map(ResultSet rs) throws SQLException
{
List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
try
{
if (rs != null)
{
ResultSetMetaData meta = rs.getMetaData();
int numColumns = meta.getColumnCount();
while (rs.next())
{
Map<String, Object> row = new HashMap<String, Object>();
for (int i = 1; i <= numColumns; ++i)
{
String name = meta.getColumnName(i);
Object value = rs.getObject(i);
row.put(name, value);
}
results.add(row);
}
}
}
finally
{
close(rs);
}
return results;
}
}
Your problem is that DriverManager.getConnection returns a java.sql.Connection. Since your class is also called Connection you are getting a name clash between lokate.Connection and java.sql.Connection. You Will need to specify the fully qualified class name where ever you want to use java.sql.Connection, otherwise lokate.Connection is assumed.
Specify fully qualified class name like so:
java.sql.Connection con = null;
// ....
con = DriverManager.getConnection(connectionUrl);
Alternatively, rename your Connection class to something else and you will not get this naming conflict.
Connection is an existing type in the java.sql package, which is what DriverManager.getConnection returns. You have named your class as Connection too, so this is causing the confusion. The simplest way out would be to rename your class to something else and add an import java.sql.Connection; at the top.
Also, is it best practice to put the connection in the class like this then call a new object every time I want to do something with the db?
I think the best practice would be to use an existing solution to this problem, so that you can avoid re-inventing the wheel and focus on what makes your problem unique.
If you are writing a server application (it isn't clear whether you are), then I would also consider using a database connection pool. Creating new database connections on the fly is not efficient and doesn't scale well. You can read about database connection issues in this article I wrote a while back.