ResultSet is Closed - java

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")+")");
}

Related

How can you get a JTextField to work in a SQLite Select statement?

I am working on a program which will when finished allow the end user to keep track of there sound packs in a database through SQLite. The newest problem I am running into is that I can not get the Select statement to take a JTextField input. The reason that I want to do this is that I already have the text fields linked through the insert method. I have tried switching the variable types in the readAllData method and I am not entirely sure what other way to fix it.
The fields are as follows
PackId
PackName
VendorName
PackValue
what I want to happen is when I hit the Update button I want the data in the database to print out to the console (for now) and I am also going to be adding a select specified records method as well.
Here is the code I do apologize in advance this is a very long project:
public void readAllData() throws SQLException {
Connection conn = DriverManager.getConnection("jdbc:sqlite:packsver3.db");
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT * FROM packs";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()) {
String PackId = PackId.getText();
String PackName = PackName.getText();
String VendorName = VendorName.getTextField();
String PackValue = rs.getTextField;
System.out.println("All Packs\n");
System.out.println("PackId: " +PackId);
System.out.println("PackName: " +PackName);
System.out.println("VendorName: " +VendorName);
System.out.println("PackValue: " +PackValue+"\n\n");
}
} catch (SQLException e) {
System.out.println(e.toString());
}finally {
try {
assert rs != null;
rs.close();
ps.close();
conn.close();
} catch (SQLException e) {
System.out.println(e.toString());
}
Console Output

How to store output of MySQL command into a java variable (JDBC)

I was wondering how to issue a MySQL command that checks if a table within my database is empty and then subsequently store the boolean result into a java variable. I am trying to use JDBC commands to do this.
This is what I have so far but it is not working properly:
#Override
public boolean isEmpty(Connection connection) {
Statement statement = null;
ResultSet resultSet = null;
Boolean var = true;
try {
statement = connection.createStatement();
System.out.println(statement.execute("SELECT EXISTS (SELECT 1 FROM Persons) AS OUTPUT"));
if(statement.execute("SELECT EXISTS (SELECT 1 FROM Persons)")) {
var = false;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return var;
}
When I run the program with a completely new, unpopulated mySQL table, the function returns true. Does anyone know a solution?
Your test checks if the table exists, instead you want to see if the table contains any rows. In order to do so, select the count of rows from the table and verify it is greater than 0. Prefer PreparedStatement over Statement (it's more efficient and performant), and you need a ResultSet to actually iterate the result from the server. Something like,
#Override
public boolean isEmpty(Connection connection) {
PreparedStatement statement = null;
ResultSet resultSet = null;
boolean res = false; // no need for the wrapper type here.
String sql = "SELECT COUNT(*) FROM Persons";
try {
statement = connection.prepareStatement(sql);
System.out.println(sql);
resultSet = statement.executeQuery();
if (resultSet.next()) {
res = resultSet.getInt(1) > 0;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return res;
}
Changed var to res because (as of Java 10) var is now a keyword in Java.
As you are executing a select statement, instead of using Statement.execute(..), you should use Statement.executeQuery(..) and iterate over its result set.
The boolean return value from execute(..) indicates if the first value is a result set or not. It is not the boolean column from your query. You should normally only use execute(..) if you don't know what type of statement it is, or if it is a statement that can produce multiple results (update counts and result sets).
So, instead use:
boolean exists;
try (ResultSet rs = statement.executeQuery("SELECT EXISTS (SELECT 1 FROM Persons)")) {
exists = rs.next() && rs.getBoolean(1);
}

JPA number generators without primary key and general use

Environment: OpenJPA2.x, Tomcat webapp, Mariadb but may be changed. This is not Hibernate or Spring webapp.
I have read few topics already such as this:
Hibernate JPA Sequence (non-Id)
I have few entity classes with someNumber non-primary key field, some have someNumber and someNumberB twin columns. Fields have a constraint UNIQUE(someNumber) and UNIQUE(someNumberB), primary key composite is PRIMARY(server_id, code). I need a numeric value before commiting row inserts.
If I understood other topics I cannot use JPA #generator tags. I am forced to implement an old-school utility method. This is a method I did, it takes a fresh db connection so it's always run in a separate transaction.
public synchronized static long getGeneratorValue(String genName, int incStep) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
if (genName==null) genName="app";
// try few times before give up, another node may have updated a value in-between. Create a new transaction from db connection pool.
for(int idx=0; idx<3; idx++) {
conn = createConnection(false); // autocommit=false
stmt = conn.createStatement();
rs = stmt.executeQuery(String.format("Select value From generators Where name='%s'", genName));
if (!rs.next()) throw new IllegalArgumentException("Invalid generator name " + genName);
if (incStep==0)
return rs.getLong("value"); // return an existing value
long oldValue = rs.getLong("value");
long newValue = oldValue+incStep;
int rowCount = stmt.executeUpdate(String.format("Update generators Set value=%d Where name='%s' and value=%d", newValue, genName, oldValue));
if (rowCount>0) {
conn.commit();
return newValue;
}
close(rs, stmt, conn);
conn=null;
}
throw new IllegalArgumentException("Obtaining a generator value failed " + genName);
} catch(Exception ex) {
try { conn.rollback(); } catch(Exception e){}
if (ex instanceof IllegalArgumentException) throw (IllegalArgumentException)ex;
else throw new IllegalArgumentException(ex.getMessage(), ex);
} finally {
if (conn!=null) close(rs, stmt, conn);
}
}
I am not fully happy with this, especially a failsafe foreach_loop against another Tomcat node updated generator value in-between concurrently. This loop may fail on busy workloads.
Could I use DB auto_increment column as a general purpose number generator, I suppose it tolerates better concurrency? If this locks a database to MariaDB,MySQL or similar I can live with that for now.
Value must be a numeric field for legacy purpose, I cannot use GUID string values.
I was thinking about using DB auto_increment column and came up with this utility function. I probably go with this implementation or do StackOverflow community have better tricks available?
CREATE TABLE generatorsB (
value bigint UNSIGNED NOT NULL auto_increment,
name varchar(255) NOT NULL,
PRIMARY KEY(value)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_swedish_ci;
// name=any comment such as an entityClass name, no real application use
public static long getGeneratorBValue(String name) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
String sql = String.format("Insert Into generatorsB (name) Values('%s')", name);
conn = createConnection(false); // autocommit=false
stmt = conn.createStatement();
int rowCount = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
if (rowCount>0) {
rs = stmt.getGeneratedKeys();
if (rs.next()) {
long newValue = rs.getLong(1);
if (newValue % 5 == 0) {
// delete old rows every 5th call, we don't need generator table rows
sql = "Delete From generatorsB Where value < "+ (newValue-5);
stmt.close();
stmt = conn.createStatement();
stmt.executeUpdate(sql, Statement.NO_GENERATED_KEYS);
}
conn.commit();
return newValue;
}
}
throw new IllegalArgumentException("Obtaining a generator value failed");
} catch(Exception ex) {
try { conn.rollback(); } catch(Exception e){}
if (ex instanceof IllegalArgumentException) throw (IllegalArgumentException)ex;
else throw new IllegalArgumentException(ex.getMessage(), ex);
} finally {
if (conn!=null) close(rs, stmt, conn);
}
}

one base executeQuery method or one for every query

I've started creating a toDoList and I like to create a "DataMapper" to fire queries to my Database.
I created this Datamapper to handle things for me but I don't know if my way of thinking is correct in this case. In my Datamapper I have created only 1 method that has to execute the queries and several methods that know what query to fire (to minimalize the open and close methods).
For example I have this:
public Object insertItem(String value) {
this.value = value;
String insertQuery = "INSERT INTO toDoList(item,datum) " + "VALUES ('" + value + "', CURDATE())";
return this.executeQuery(insertQuery);
}
public Object removeItem(int id) {
this.itemId = id;
String deleteQuery = "DELETE FROM test WHERE id ='" + itemId + "'";
return this.executeQuery(deleteQuery);
}
private ResultSet executeQuery(String query) {
this.query = query;
Connection con = null;
Statement st = null;
ResultSet rs = null;
try {
con = db.connectToAndQueryDatabase(database, user, password);
st = con.createStatement();
st.executeUpdate(query);
}
catch (SQLException e1) {
e1.printStackTrace();
}
finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e2) { /* ignored */}
}
if (st != null) {
try {
st.close();
} catch (SQLException e2) { /* ignored */}
}
if (con != null) {
try {
con.close();
} catch (SQLException e2) { /* ignored */}
}
System.out.println("connection closed");
}
return rs;
}
So now I don't know if it's correct to return a ResultSet like this. I tought of doing something like
public ArrayList<ToDoListModel> getModel() {
return null;
}
To insert every record returned in a ArrayList. But I feel like I'm stuck a little bit. Can someone lead me to a right way with an example or something?
It depends on the way the application works. If you have a lot of databases hits in a short time it would be better to bundle them and use the same database connection for all querys to reduce the overhead of the connection establishment and cleaning.
If you only have single querys in lager intervals you could do it this way.
You should also consider if you want to seperate the database layer and the user interface (if existing).
In this case you should not pass the ResultSet up to the user interface but wrap the data in an independent container and pass this through your application.
If I understand your problem correctly!, you need to pass a list of ToDoListModel objects
to insert into the DB using the insertItem method.
How you pass your object to insert items does not actually matter, but what you need to consider is how concurrent this DataMapper works, if it can be accessed by multiple threads at a time, you will end up creating multiple db connections which is little expensive.Your code actually works without any issue in sequential access.
So you can add a synchronized block to connection creation and make DataMapper class singleton.
Ok in that case what you can do is, create a ArrayList of hashmap first. which contains Key, Value as Column name and Column value. After that you can create your model.
public List convertResultSetToArrayList(ResultSet rs) throws SQLException{
ResultSetMetaData mdata = rs.getMetaData();
int columns = mdata.getColumnCount();
ArrayList list = new ArrayList();
while (rs.next()){
HashMap row = new HashMap(columns);
for(int i=1; i<=columns; ++i){
row.put(md.getColumnName(i),rs.getObject(i));
}
list.add(row);
}
return list;
}

why does executeUpdate return 1 even if no new row has been inserted?

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.

Categories

Resources