I am attempting at writing my own server as a personal project, however I'm running into some issues. I've finally completed the setup for a packet system between the Java server and the C# client which makes me very happy, even though I've had some help. Anyway, here's the code that I've written trying to get this to work properly. I created the SQLManager using static variables, because I read that the database connection should be static, if this is incorrect, please let me know.
Here's the error:
Exception in thread "main" java.lang.NullPointerException
com.fmeg.server.util.SQLManager.runQuery(SQLManager.java:37)
Here's my SQL Class:
public static boolean connectToDatabase() {
try {
connection = DriverManager.getConnection(host, credentials[0], credentials[1]);
connected = true;
} catch (Exception e) { connected = false; }
Misc.log("Database: " + database + " || Connection State: " + connected);
return connected;
}
public static boolean runQuery(String query) {
try {
ResultSet rs = checkQuery(query);
if(rs == null)
Misc.error("Result Set returned null.");
if(rs.next())
Misc.log("Current Row: " + rs.getRow());
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static ResultSet checkQuery(String query) throws SQLException {
try {
Misc.log(query);
return statement.executeQuery(query);
} catch (Exception e) {
destroyConnection();
return null;
}
}
private static void destroyConnection() {
try {
statement.close();
connection.close();
connected = false;
Misc.error("Database connection destroyed!");
} catch (Exception e ) { }
}
Apparently, the ResultSet is returning null, here's the output in the console.
[LOG]: Database: Unity3D || Connection State: true
[LOG]: Server <Test Realm> Successfully Started on port: 9955!
[LOG]: select * from `accounts`
[ERROR]: Result Set returned null.
Here's where I'm calling the query:
SQLManager.runQuery("select * from \'accounts\'");
Any pointers would be greatly appreciated, as I don't exactly see what the problem is. To answer these questions if the do arise, yes I do have a table called accounts, and yes it does have entries.
You have a syntax error on table name. Table names should not be a literal but should be quoted with back ticks. These back ticks are optional unless table name has any special chars in it or if it is a reserved word.
Just because of this error, the statement
return statement.executeQuery(query);
is causing an Exception and the method was returning a null for ResultSet.
You better catch the exception and see what the stacktrace says on it.
Change:
QLManager.runQuery("select * from \'accounts\'");
To:
QLManager.runQuery("select * from `accounts`");
You problem in this code:
if(rs == null) {
Misc.error("Result Set returned null.");
if(rs.next())
Misc.log("Current Row: " + rs.getRow());
In case any exception occurred in checkQuery method, it will return a null for ResultSet, then the code will proceed to rs.next(), which rs null, then a NullPointerException will raise.
What all you have to do is:
if(rs == null) {
Misc.error("Result Set returned null.");
return false;
}
if(rs.next()) {
Misc.log("Current Row: " + rs.getRow());
return true;
}
But you have to at least to log the error or throw the exception in checkQuery to figure out what is the exact problem that you are facing. not just return null object.
Related
I'm using SQL to compare a date of an event
public static boolean sameDate(String DateString)
{
PreparedStatement statement = Application.database.newStatement("SELECT * FROM BookShift WHERE id = ? IN (SELECT id FROM Class WHERE Date = ?)");
try
{
if(statement !=null)
{
statement.setInt(1, main.id);
statement.setString(2, DateTwo);
ResultSet results = Application.database.runQuery(statement);
if (results != null)
{
return true;
}
else {
return false;
}
}
}
catch (SQLException resultsexception)
{
System.out.println("Database result processing error: " + resultsexception.getMessage());
}
return false;
}
Whenever I run my program and try booking a new shift, regardless of whether it does clash or not it always returns that it does.
You need to check if the result set has any rows or not, not check if it is null.
if (results.next())
{
return true;
}
else {
return false;
}
or, of course, just
return results.next();
As the comments say, it is not generally correct to check if results is null. I don't have the SQL experience to tell you what runQuery() will return on a query that fails, but I doubt that it's null, I would expect it to return an empty ResultSet.
Checking if it's null first isn't a bad thing, and in fact is a good idea to avoid NullPointerException throws. However, it's not enough to just use that check.
I am moving from PHP to Java and I'm a little struggled.
I have this method like this that I use to get some data from MySQL database and I would like to treat the failure if no data got from database.
public double getRate() {
double ret;
try {
// do a select query
PreparedStatement stmt = conn.prepareStatement("SELECT `rate` FROM `rates` LIMIT 1");
ResultSet rs = stmt.executeQuery();
// get result
rs.absolute(1);
ret = rs.getDouble(1);
// close the resources
rs.close();
stmt.close();
}
// this catches only the SQL errors (if I am right)
catch (SQLException ex) {
ex.printStackTrace();
}
// THIS IS WRONG BECAUSE "variable ret might not have been initialized"
return ret;
}
In PHP we can return whatever in case of failure like this:
<?php
public function getRate() {
$ret = db::getOne("SELECT `rate` FROM `rates` LIMIT 1");
if ($ret) {
return $ret; // row has been found, so we return it
} else {
return false; // row hasn't been found, so we return false --> THIS is what I need to do in Java
}
}
?>
So how to treat a failure in Java methods/functions where I have nothing to return?
You have several options:
throw an exception, and catch this by the code which calls the method. This works well, is a nice way to handle it. But requires a lot of additional try-catch statements
Return -1 on error. This is also a very common way to do if you work with natural numbers only
always return a result object, which contains a the output and a success/error status
Use the Double Class instead, and return null on fail
In Java, you can't return double from one place and boolean from another. What you could do is, initialize your Double (wrapper of double primitive) value like:
Double ret = null;
And if there are no rows or any SQLException, you would return this value back to caller. In called method you could do something like:
Double rate = getRate();
if (rate == null) {
//no row found
} else {
//i have the row. continue with business logic
}
You could make your method return an object of the double wrapper class Double. Then you could return a null pointer in case of some failure.
public Double getRate() {
...
if(ok)
return new Double(ret);
else
return null;
}
Initialize your double variable with a control value. If it is not changed when exiting the method, then something went wrong.
The control value can be something you do not expect to get from the query, so for rates it could be a negative number, say -1 since rates can't be negative.
double ret=-1.00d;
I am adding a sample code so you can understand how to handle such scenarios.
If your default value does not changes,it means there was nothing that matched your query.
public double methodName(int arg)
{
double risk=0.0;
String query = null;
PreparedStatement stm = null;
ResultSet r = null;
Connection con=null;
try{
con=ConnectionDB.getConnection();
if(con!=null)
{
query="select risk from table where year="+arg;
stm = con.prepareStatement(query);
r = stm.executeQuery();
if(r.next())
{
risk=r.getDouble(1);
}
}
}catch(Exception e)
{
e.printStackTrace();
}
finally{
try {
if (r != null) {
r.close();
}
if (stm != null) {
stm.close();
}
if(con!=null)
{
con.close();
}
} catch (Exception e) {
System.out.println("" + e);
}
}
return risk;
}
You could return an OptionalDouble, which makes it obvious to the caller that they need to handle the case of the result not being found:
try {
// get ret
return OptionalDouble.of(ret);
} catch (SQLException ex) {
return OptionalDouble.empty();
}
In your Java example, when you talk about a "failure", you are talking about an unexpected error (e.g. a SQL Exception, a non-expected error in the DB access).
Nevertheless, in your PHP example, when you talk about a "failure", you are talking about a normal scenario (No data in database).
So, both examples are quite different.
In my opinion, if I get an unexpected sitution, I wouldn't return any value, I'd throw an exception. I usually return null, -1 and this kind of values in normal and expected scenarios where there isn't data to return.
Following is my Table Definition:
create Table alarms(
alarmId int primary key identity(1,1),
alarmDate varchar(50) not null,
alarmText varchar(50) not null,
alarmStatus varchar(10) Check (alarmStatus in(-1, 0, 1)) Default 0
);
Secondly here are some of my methods i'm using:
public void restartDatabase(){
try{
Class.forName(Settings.getDatabaseDriver());
connection = DriverManager.getConnection( Settings.getJdbcUrl() );
statement = connection.createStatement();
}
catch(Exception e){
e.printStackTrace();
}
}
public ResultSet executeQuery(String query){
ResultSet result = null;
try {
result = statement.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
public void closeDatabase() {
try {
if ((statement != null) && (connection != null)) {
statement.close();
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
What i want to do is to get all the alarmId's from the table where date is equal to the given date and then against each alarmId i want to update its status to given status:
public static void updateAlarmStatus(int status) {
ResultSet rs = null;
database.restartDatabase();
try {
rs = database
.executeQuery("Select alarmId from alarms where alarmDate = '"
+ Alarm.getFormattedDateTime(DateFormat.FULL,
DateFormat.SHORT) + "'");
while (rs.next()) {
database.executeUpdate("update alarms set alarmStatus = '"+status+"' where alarmId = '"+rs.getString("alarmId")+"'");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
database.closeDatabase();
}
}
But it generates the Error that Result Set is Closed.
I Goggled it and came to know that a result set automatically closes when we try to execute another query inside it
and it needs to restart the connection.
i tried calling restartDatabase() method that is creating new connection but still getting the same error.
I'm guessing executeUpdate uses the same instance variable for its Statement as the query uses. When you create a new Statement and assign it to the variable, nothing is referring to the old one, so it gets cut loose and becomes subject to garbage-collection. During garbage collection the statement's finalizer is invoked, closing it. Closing the statement makes the ResultSet it created close as well.
You shouldn't be sharing these Statement variables between different queries and updates. The statement should be a local variable and not a member of an object instance.
Also, result Sets should always be local variables, they shouldn't be passed outside the method where they're created. The resultSet is a reference to a cursor, it doesn't actually hold any data. Always have your code read from the resultSet and populate some data structure with the results, then return the data structure.
You can also select and change all alarmIds at once:
rs = database.
executeQuery("Select group_concat(distinct alarmId) as alarmIds from alarms group by alarmDate having alarmDate = '"
+ Alarm.getFormattedDateTime(DateFormat.FULL,
DateFormat.SHORT) + "'");
while (rs.next()) { // there will be only one result
database.executeUpdate("update alarms set alarmStatus = '"+status+"' where alarmId in ("+rs.getString("alarmIds")+")");
}
here is my very simple table (Postgres):
CREATE TABLE IF NOT EXISTS PERFORMANCE.TEST
(
test text NOT NULL UNIQUE
);
if I try to insert a String using the command below FROM the database,everything works as expected, not surprisingly a new row appears in the DB.
insert into performance.test (test) values ('abbbbaw');
However if I want to insert a String through JDBC, nothing gets inserted, although preparedStatement.executeUpdate() always returns 1.
Below is my method that should be working but it does not. Please tell me if I am missing something obvious.
I want to add that I never get any SQLException.
private void storePerformance() {
Connection conn= initializePerformanceConnection();
if (conn!= null) {
PreparedStatement insertPS = null;
try {
insertPS = conn.prepareStatement("insert into performance.test (test) values (?)");
insertPS.setString(1, queryVar);
int i = insertPS.executeUpdate();
LogManager.doLog(LOG, LOGLEVEL.INFO," numberofrows= "+i);
} catch (SQLException e) {
LogManager.doLog(LOG, LOGLEVEL.INFO,"Inserting query failed = "+queryVar,e);
}finally{
if(insertPS != null){
try {
insertPS.close();
} catch (SQLException e) {
LogManager.doLog(LOG, LOGLEVEL.INFO,"Closing PreparedStatement failed = "+queryVar,e);
}
}
try {
conn.close();
} catch (SQLException e) {
LogManager.doLog(LOG, LOGLEVEL.INFO,"Closing performanceConnection failed= "+ queryVar, e);
}
}
}
}
that was missing:
conn.commit();
(after the executeUpdate())
actually a new row was inserted but the DB rolled back immediately.
executeupdate is for a 'update table set column = value so on'. For insert just call execute of PreparedStatement.
How can I make transaction in java+sqlite3 i check google already.I want to make it to be rollback statement so if somewhere in statement goes wrong all statement go to "space" and the DB got rollback to moment before start transaction.
Something like that here Roolback-stmt I hope my question is understandable. So remember that English is not my native language. Here is part of code that I want to make to be a statement:
ArrayList <String> al = new ArrayList <String> ();
try {
// transaction begin;
ResultSet rs = stat.executeQuery("select _id, godziny_id from tblZmiany where harmonogram_id = " + h_id + " order by pracownik_id, Dzien");
while (rs.next()) {
al.add("insert into tblWykonanie (Zmiana_id, Godziny_id) values ('" + rs.getLong(1) + "', " + rs.getInt(2) + ");");
}
for (String s : al)
if (stat.executeUpdate(s) < 1) {
// Jeśli insert nie wstawił kolejnego rekordu
//rollback;
error_code = "Wystąpił problem podczas zatwierdzania harmonogramu.";
return false;
}
// commit;
} catch (SQLException e) {
e.printStackTrace();
System.out.println(e.getMessage());
return false;
} catch (Exception e) {
error_code = e.getMessage();
return false;
}
return true;
You can do this one of two ways, at the connection level or via batch statements.
At the connection level:
final boolean oldAutoCommit = stat.getConnection().getAutoCommit();
stat.getConnection().setAutoCommit(false);
try {
// Your update and insert code here
} catch(Exception e) {
stat.getConnection().rollback();
} finally {
stat.getConnection().commit();
stat.getConnection().setAutoCommit(oldAutoCommit);
}
Error handling has been omitted from the pseudocode, but you get the general idea. You may wish to avoid messing around with the connection, and to instead use batch statements:
for(String query: queries) {
stat.addBatch(query);
}
try {
stat.executeBatch();
} catch(Exception e) {
// ...
}
Which is alot cleaner. Remember that you can always check wether your DB/Driver supports batched transaction via DatabaseMetaData.supportsBatchUpdates().
Use a JDBC driver like SQLiteJDBC which supports transactions.