The correct way to update/query from DB - java

I am abit new with working with DatatBases from Java, and I have been wondering if I am working in a correct way. In my code all the DB interface is done within one class called DataAccess, an example of my code:
Please note that I opened a connection (connBlng) before entering the function isValidOperator().
Is this the correct way to work or should I open and close a connection everytime I need to access the DB?
if(da.StartBlngConnection() == null)
return "ERROR"
DataAccess da = new DataAccess();
da.isValidOperator("123")
//this is code from DataAccess Class
public Boolean isValidOperator(String dn) {
System.out.println( npgReqID + " - " + LOG_TAG + "inside isValidOperator : " + dn);
PreparedStatement prepStmt = null;
ResultSet queryResult = null;
try{
prepStmt = connBlng.prepareStatement("select network_id, network_identifier from operators where network_identifier = ?");
prepStmt.setString(1, dn);
queryResult = prepStmt.executeQuery();
if (queryResult.next()) {
return true;
}
}catch(Exception e){
System.out.println(npgReqID + " - " + e);
DBLog("", new Date(),"" , npgReqID , "" ,"" , MakeSureNotOutOfRange(GetStackTrace(e),4000), "" , "");
return false;
} finally{
closeStatmentandRS(queryResult, PreparedStatement);
}
return false;
}

JDBC plain is not a very easy to use API. Maybe you can take a look at Dalesbread https://github.com/Blackrush/Dalesbred which is a lightwight JDBC wrapper. Here is a discussion about JDBC wrappers in common: simple jdbc wrapper.
I would not suggest to close the DB connection all the time, you should use pooling for that. Normally creating a DB connection is expensive.

Related

Best way to use an SQL connection in Java

My question is similar to this one, but more specific.
I have multiple classes that execute queries against the same database. The problem is that the host may occasionally experience problems with the Internet connection, and my application must be designed against that knowledge.
So I cannot just create java.sql.Connection once, because it can break at any time. If a connection breaks while executing one of the queries - it's OK for me. The easiest way is to just create a new one in each query method:
public static Profile getProfileBySteamID(long userid) {
try (Connection connection = DriverManager.getConnection(Globals.dbConnection);
Statement st = connection.createStatement()) {
final ResultSet res = st.executeQuery("SELECT " +
"p_id, p_name, p_id_dis, p_uid, " +
"p_lastupd, p_lastservertime, p_roles " +
"FROM profiles WHERE p_uid='" + userid + "'");
if (!res.next())
throw new RuntimeException("No such profile");
long disID = res.getLong("p_id_dis");
String steamid = res.getString("p_uid");
return new Profile(steamid, disID);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static Profile getProfileByDisID(long userid) {
try (Connection connection = DriverManager.getConnection(Globals.dbConnection);
Statement st = connection.createStatement()) {
final ResultSet res = st.executeQuery("SELECT " +
"p_id, p_name, p_id_dis, p_uid, " +
"p_lastupd, p_lastservertime, p_roles " +
"FROM profiles WHERE p_id_dis='" + userid + "'");
if (!res.next())
throw new RuntimeException("No such profile");
long disID = res.getLong("p_id_dis");
String steamid = res.getString("p_uid");
return new Profile(steamid, disID);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// ... and about 7 more methods like these
I'm OK with the possible performance issues (because these queries are executed not so frequently), but this is too much repeated code that is hard to extract to other private methods, and, you know, kinda looks ugly.
So my question is how to get that java.sql.Statement (or better - PreparedStatement) in as short expression as possible, and without the try-catch statements if possible. Maybe I should consider using ConnectionPool?
I'd like to get some examples of the solution, or links to source code examples of ConnectionPool with PreparedStatements usage or anything similar
Write a helper method which does the common code:
public class DBHelper {
public interface SqlFunction<T> {
T apply(Statement statement) throws SQLException;
}
public static <T> T withStatement(SqlFunction<T> task) {
try (Connection connection = DriverManager.getConnection(Globals.dbConnection);
Statement st = connection.createStatement()) {
return task.apply(st);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
Profile p = DBHelper.withStatement(st -> {
final ResultSet res = st.executeQuery("SELECT " +
"p_id, p_name, p_id_dis, p_uid, " +
"p_lastupd, p_lastservertime, p_roles " +
"FROM profiles WHERE p_id_dis='" + userid + "'");
if (!res.next())
throw new RuntimeException("No such profile");
long disID = res.getLong("p_id_dis");
String steamid = res.getString("p_uid");
return new Profile(steamid, disID);
});
}
}
By the way, you should be using PreparedStatement -- it handles escaping parameters, so it is both easier to use and safer against malicious SQL injection.
Using a connection pool, e.g. Hikari, is also a good idea in addition to the approach I suggest above. It will handle broken connections and reduce the overhead of making new connections. It will also allow you to limit the number of concurrent connections your application will use.

Derby DB table creation and connection

I am writing a set of Eclipse console-based word games and have integrated an embedded Derby DB driver for storing a users result history.
My question is regarding Derby table initialization.
The Derby Database connection itself has a condition checker:
"jdbc:derby:dbName;create=true" So if the DB exists, it connects, if not it creates.
I am stuck on how to do this with a TBL in the Database. Even with the help from this similar question: how to create table if it doesn't exist using Derby Db
I've included my code below. My code throws a sql exception at the query for the Table 'RPS'. (My dbName is 'RPSdb' and TBL name is 'RPS'.)
This exception is caught by my catch block and passed to a static method in a different class ('DerbyHelper'). This static method is setup right now to always return true for the time being.
My question is how to I code my catch block and corresponding helper class so that I can implement the above mentioned TBL functionality?
Connection conn = null;
ArrayList<Statement> statements = new ArrayList<Statement>(); // list of Statements, PreparedStatements
Statement s;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(protocol + dbName + ";create=true");
conn.setAutoCommit(false);
s = conn.createStatement();
statements.add(s);
rs = s.executeQuery("select * from RPS");
rs.next();
int cG = rs.getInt(1) + corGuess;
int iCG = rs.getInt(2) + inCorGuess;
s.executeUpdate("UPDATE RPS SET corGuesses = " + cG
+ ", inCorGuesses= " + iCG);
conn.commit();
}
catch( SQLException e ) {
if( DerbyHelper.tableAlreadyExists( e ) ) {
// what do I do here??
}
}
Interface java.sql.Connection has method getMetaData() which returns java.sql.DatabaseMetaData and that interface contains method getTables() which will tell you whether your database table exists, or not.

Java SQL Server Application Stuck On statement.executeUpdate()

I have a rather annoying issue. In the piece of code below, I am trying to insert a new row to the "RevisionDispersion" table in my database. However, whenever I call stmt.executeUpdate() the program freezes and there ends up being no transaction to the database. No matter how long I wait; the database just won't be updated. Below is the code of interest:
private static final String INSERT_DISPERSION = "insert into RevisionDispersion("
+ Assignments.ID + ", "
+ Persons.EMAIL + ", "
+ Handins.ID + ")"
+ " values(?, ?, ?)";
public static void disperse(DataSource source, Assignment assignment) throws Exception
{
List<String> handins = assignment.getHandins();
//used to decide who checks which assignment
int maxRNG = Math.max(1, handins.size() / assignment.getPeerCount());
int rng = new Random().nextInt(maxRNG);
PreparedStatement stmt = null;
Connection con = null;
try{
//Get the connection, set it to TRANSACTION_SERIALIZABLE and set autocommit to false
con = source.getConnection();
configureConnection(con);
//Prepare the statement to insert the new dispersion
stmt = con.prepareStatement(INSERT_DISPERSION);
stmt.setString(1, assignment.getID());
//Iterate over all hand-ins and decide from which peer a peer receives feedback
for(int i = 0; i < handins.size(); i++)
{
HandIn handin = new HandIn(source.getConnection(), handins.get(i));
String student = handin.getEmail();
stmt.setString(2, student);
for(int j = 1; j <= assignment.getPeerCount(); j++)
{
HandIn otherHandin = new HandIn(source.getConnection(), handins.get(j * rng));
stmt.setString(3, otherHandin.getId());
stmt.executeUpdate();
}
}
con.commit();
}catch(Exception e){
throw e;
}finally{
closeQuietly(con, stmt);
}
}
//This method is originally in the DBAO class, but I put it here for you folks.
protected static void configureConnection(Connection connection) throws SQLException
{
connection.setAutoCommit(false);
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
}
This problem occurs in no other places in the application. Whenever I run the SQL statement in SQL Server Management Studio, with identical parameters, it does not get stuck and it inserts the new rows just fine. After deleting the rows and trying the same in the application, it gets stuck.
Can anyone point me in the right direction of what is going wrong? I've been trying for 3 hours straight now...
Stuff I already tried
-use stmt.addBatch() rather than executeUpdate() (did not make a difference. It would get stuck at executeBatch())
-Check if all connections are being closed properly; they are.
-Check if other statements/resultsets are still open that use RevisionDispersion table (there are none still open. Even if there were, should not make a difference I think?)
-Completely delete the database and set it back up
I solved the issue...
In a different piece of code I had the following:
private static final String GET_NOT_DISPERSED = "select * from Assignments where "
+ Assignments.CLOSE_DATE + "<=? and "
+ Assignments.PEER_START_DATE + ">=? and "
+ Assignments.ID + " not in(select " + Assignments.ID + " from RevisionDispersion)";
private void makeMailDispersion() throws Exception
{
DateTime currentDate = DateTime.getCurrentDateTime();
PreparedStatement assignmentsStmt = null;
ResultSet assignments = null;
Connection con = null;
try{
con = source.getConnection();
configureConnection(con);
assignmentsStmt = con.prepareStatement(GET_NOT_DISPERSED);
assignmentsStmt.setString(1, currentDate.toString());
assignmentsStmt.setString(2, currentDate.toString());
assignments = assignmentsStmt.executeQuery();
ArrayList<Assignment> requiresDispersion = new ArrayList<>();
assignments.close();
assignmentsStmt.close();
while(assignments.next())
{
Assignment assignment = new Assignment(source.getConnection(), assignments.getString(Assignments.ID));
AssignmentDisperser.disperse(source, assignment);
}
}catch(Exception e){
throw e;
}finally{
closeQuietly(con, assignmentsStmt, assignments);
}
}
In this piece of code, I closed the variables 'assignments' and 'assignmentsStmt'. I thought this would be sufficient to unlock the table after having used the GET_NOT_DISPERSED query. Apparently it was not: the table was still locked.
What I had to do in order to fix it: aside from calling assignments.close() and assignmentsStmt.close() I also had to call con.close(). That completely unlocked the table and allowed the code to run properly.

is JDBC code tied for single database type?

Consider this scenario: During development, I want to use MySQL and in production I will be using derby.
To get connection, I have this method from java tutorial :
public Connection getConnection() throws SQLException {
Connection conn = null;
Properties connectionProps = new Properties();
connectionProps.put("user", this.userName);
connectionProps.put("password", this.password);
if (this.dbms.equals("mysql")) {
conn = DriverManager.getConnection(
"jdbc:" + this.dbms + "://" +
this.serverName +
":" + this.portNumber + "/",
connectionProps);
} else if (this.dbms.equals("derby")) {
conn = DriverManager.getConnection(
"jdbc:" + this.dbms + ":" +
this.dbName +
";create=true",
connectionProps);
}
System.out.println("Connected to database");
return conn;
}
Here is some more code that involves querying the db (again from java tutorial):
public static void viewTable(Connection con, String dbName)
throws SQLException {
Statement stmt = null;
String query = "select COF_NAME, SUP_ID, PRICE, " +
"SALES, TOTAL " +
"from " + dbName + ".COFFEES";
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String coffeeName = rs.getString("COF_NAME");
int supplierID = rs.getInt("SUP_ID");
float price = rs.getFloat("PRICE");
int sales = rs.getInt("SALES");
int total = rs.getInt("TOTAL");
System.out.println(coffeeName + "\t" + supplierID +
"\t" + price + "\t" + sales +
"\t" + total);
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}
I am wondering if I need to change my query construction depending on what database I use at backend.
For example, MySQL and PostgreSql seems to have some key differences in queries. (In the above example it might not make any difference).
I have django framework background, where ORM is designed to handle any kind of database. same code works fine with any database plugged in.
Since you are directly writing the SQL statements when using JDBC, these are inherently tied to the database you are using. The only way around this is to use only SQL constructs which are supported by all target databases. There is nothing shielding you from the differences between databases.
Your code can have both DB-independent and DB-dependent parts.
Database-dependent parts: The JDBC URLs, SQL statements (the syntax) are database-dependent in general. With some effort, it is possible to write SQL syntax which is recognized by all Databases, but this may not be possible in all cases.
Database-independent parts: The Java code used to get the connections, run statements, managing connections, creating data sources, connection pooling etc. are (as far as I think) common to all database systems that are JDBC compliant.
Strictly speaking, the 'JDBC' code is mostly independent of the database. What changes with every database is the statement syntax (but JDBC does not specify the syntax in the first place)
I agree with the other posters that if you use standard sql, then it should be simple to switch database vendor.
There are lots of ORM's and mappers that create standard sql. My favorite is sormula (I'm the author). I recently worked on a project using sormula where test server used HSQLDB and production server used PostgreSQL.

JAX-WS #WebMethod Return User-Defined Data Type

I'm trying to create a jax-ws web service that would query a mysql database and return user-defined datatype from the #WebMethod. I first tried returning a sql.ResultSet which is provided by the sql import. However, JAXB couldn't bind it. So I tried returning a String[][], but when reading the result in the client it shows all null fields.
I then tried creating my own data-type and i understand that the class i create should have a default constructor and public sets and gets to all variables defined in that class. But i would like to return a resultset instead. The code to my web service is as follows:
#WebMethod
public String[][] getSummary(int month, int year){
// register jdbc driver
try
{
Class.forName("com.mysql.jdbc.Driver").newInstance();
}
catch(Exception ex)
{
System.out.print(ex.getMessage());
}
// declare connection, statement and result set
Connection connection = null;
Statement statement = null;
ResultSet resultset = null;
String[][] result = new String[30][8];
int counter = 0;
try
{
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Livestock?user=root");
statement = connection.createStatement();
resultset = statement.executeQuery("SELECT Category, Particulars, Nos, OriginalCost, AccuDep, WDV, NRV, CapLoss FROM TB_SUMMARY WHERE Month=" + month + " AND Year=" + year);
// loop over result set
while(resultset.next())
{
result[counter][0] = resultset.getString("Category");
result[counter][1] = resultset.getString("Particulars");
result[counter][2] = resultset.getString("Nos");
result[counter][3] = resultset.getString("OriginalCost");
result[counter][4] = resultset.getString("AccuDep");
result[counter][5] = resultset.getString("WDV");
result[counter][6] = resultset.getString("NRV");
result[counter][7] = resultset.getString("CapLoss");
counter++;
}
}
catch(SQLException ex)
{
System.out.println("SQLException: " + ex.getMessage());
System.out.println("SQLState: " + ex.getSQLState());
System.out.println("VendorError: " + ex.getErrorCode());
}
finally
{
// it is a good idea to release
// resources in a finally{} block
// in reverse-order of their creation
// if they are no-longer needed
if(resultset != null)
try
{
resultset.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
resultset = null;
if(statement != null)
try
{
statement.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
statement = null;
}
return result;
}
I'm not sure if the reason why all cells in the String[][] are null are because the code doesn't even reach the executeQuery(); or maybe its the jaxb binding that doesn't work.
I still don't understand how jaxb works. If anybody out there has a good example of how to use jaxws with jaxb, I would really appreciate it. The documentations i found in the internet are overwhelming and complicated.
In your scenario you won't be able to return a ResultSet because JAX-WS (JAX-B) is only able to bind simple datatypes. Have a look at this.
Your approach of returning an array is the most common to get over this problem and schould be sufficient in your case.
You should try to debug your application to make sure that your query gets fired and a result gets populated.
My best bet is that you get errors in one of the following lines:
Class.forName("com.mysql.jdbc.Driver").newInstance();
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Livestock?user=root");
But your code looks fine to me so I'm only speculating. Make sure that your DB is online and try to debug your code.
Have Fun!

Categories