I have two queries running in parallel that fetch data from my remote db
public CompletableFuture<ObservableList<Appointment>> getAppointments(){
return CompletableFuture.supplyAsync(() -> {
return queryAppointments();
});
}
public CompletableFuture<ObservableList<Customer>> getCustomers(){
return CompletableFuture.supplyAsync(() -> {
return queryCustomers();
});
}
This however causes a race condition because at the end of each query, I close the ResultSet
and Statement
so sometimes, not always, one query isn't finished and the ResultSet
is closed before the second query is done and gives me an exception:
java.sql.SQLException: Operation not allowed after ResultSet closed
First query:
public ObservableList<Appointment> queryAppointments() {
ObservableList<Appointment> listAppointments = FXCollections.observableArrayList();
Statement statement = null;
ResultSet resultSet = null;
try {
statement = ConnectionManager.getConnection().createStatement();
resultSet = statement.executeQuery(countriesQuery);
while (resultSet.next()) {
int appointmentId = resultSet.getInt("Appointment_ID");
String title = resultSet.getString("Title");
String description = resultSet.getString("Description");
//Omitted Code for creating an appointment object here...
listAppointments.add(appointment);
}
return listAppointments;
} catch (SQLException e) {
e.printStackTrace();
return null;
} finally {
ConnectionManager.closeConnection();
ConnectionManager.closeResultSetAndStatement(resultSet, statement);
}
}
Second Query:
public ObservableList<Customer> queryCustomers() {
ObservableList<Customer> listCustomers = FXCollections.observableArrayList();
Statement statement = null;
ResultSet resultSet = null;
try {
statement = ConnectionManager.getConnection().createStatement();
resultSet = statement.executeQuery(customersQuery);
while (resultSet.next()) {
int customerId = resultSet.getInt("Customer_Id");
String name = resultSet.getString("Customer_Name");
//Omitted code for creating customer object here
listCustomers.add(customer);
}
return listCustomers;
} catch (SQLException e) {
e.printStackTrace();
return null;
} finally {
ConnectionManager.closeConnection();
ConnectionManager.closeResultSetAndStatement(resultSet, statement);
}
}
public class ConnectionManager {
private static Connection connection;
public static Connection getConnection() {
try {
connection = DriverManager.getConnection(Constants.CONNECTION_URL);
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
public static void closeConnection(){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void closeResultSetAndStatement(ResultSet resultSet, Statement statement){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException sqlEx) { }
resultSet = null;
}
if (statement != null) {
try {
statement.close();
} catch (SQLException sqlEx) { }
statement = null;
}
}
How should I deal with the race condition? I've tried thinking of a solution which would be performing one query after another instead of running it in parallel.
public static void main(String[] args) {
mainRepository.getAppointments().thenAccept(retrievedAppointments -> {
mainRepository.getCustomers().thenAccept(customers -> {
});
});
}
But couldn't the ResultSet also be cut off during the second query then? Is there a better solution in doing what I'm trying to accomplish?