In the first Jframe I have a JTable filled from database and i need to pass data of the selected jTable to another frame.
So I need to know from another JInternalFrame which row was selected in the First Jframe
public void showTableData() {
try {
Class.forName(driverName);
Connection con = DriverManager.getConnection(url, userName, password);
String sql = "SELECT t.name, t.exam, l.coursename\n"
+ "FROM exam AS t\n"
+ "INNER JOIN Course as l ON (t.LendaID=l.LendaID)";
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
int i = 0;
Jtable1.setModel(DbUtils.resultSetToTableModel(rs));
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null, ex.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
}
}
This is the table I've in the first Jframe
Regardless the number of your components, as a simple solution, you can create a CourseEventDispatcher class to be central point to dispatch course events across the application.
public class CourseEventDispatcher {
private List<CourseEventSubscriber> subscribers;
// ...
public void dispatchEvent(CourseEvent event) {
for(CourseEventSubscriber: subscribers) {
if( event.getSource() != subscriber ) {
subscriber.onCourseEvent(event);
}
}
}
}
And for each relevant view, there is a controller which is a CourseEventSubscriber:
public class SomeFrameController implements CourseEventSubscriber {
private CourseEventDispatcher courseEventDispatcher;
public SomeFrameController(CourseEventDispatcher courseEventDispather) {
this.courseEventDispatcher = courseEventDispatcher;
}
public void addSelectionListener() {
// ...
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
doYourOwnStuff();
// then dispatch the event
courseEventDispatcher.dispatch(new CourseEvent(this, event));
}
}
);
}
// from some other view
public void onCourseEvent(CourseEvent event) {
// process the event
// e.g. event.getEvent()
}
}
And CourseEvent is a simple class of
public class CourseEvent {
private CourseEventSubscriber source;
private EventObject event;
public CourseEvent(CourseEventSubscriber source, EventObject event) {
this.source = source;
this.event = event;
}
// getters
}
You can add(register) your controllers after you created a dispatcher.
Hope this gives you another perspective.
Related
I'm wondering if anybody can help me with a rather annoying problem regarding creating a background thread in JavaFX! I currently have several SQL queries that add data to the UI which currently run on the JavaFX Application Thread (see example below). However when each of these queries execute it freezes the UI because it isn't running on a background thread. I've looked at various examples that use Task and sort of understand them but I cannot get them to work when doing database queries, some of which take a few seconds to run.
Here is one of the methods that executes a query:
public void getTopOrders() {
customerOrders.clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Get_Top_5_Customers_week";
ResultSet rs;
try (Statement stmt = con.createStatement();) {
rs = stmt.executeQuery(SQL);
while (rs.next()) {
double orderValue = Double.parseDouble(rs.getString(3));
customerOrders.add(new CustomerOrders(rs.getString(1),
rs.getString(2), "£" + formatter.format(orderValue),
rs.getString(4).substring(6, 8) + "/" +
rs.getString(4).substring(4, 6) + "/" +
rs.getString(4).substring(0, 4)));
}
}
} catch (SQLException | NumberFormatException e) {
}
}
Each processed record is added to an ObservableList which is linked to a TableView, or graph or simply sets the text on a label (depends on the query). How can I execute the query on a background thread and still leave the interface free to use and be updated from the queries
Thanks in advance
I created a sample solution for using a Task (as suggested in Alexander Kirov's comment) to access a database on a concurrently executing thread to the JavaFX application thread.
The relevant parts of the sample solution are reproduced below:
// fetches a collection of names from a database.
class FetchNamesTask extends DBTask<ObservableList<String>> {
#Override protected ObservableList<String> call() throws Exception {
// artificially pause for a while to simulate a long
// running database connection.
Thread.sleep(1000);
try (Connection con = getConnection()) {
return fetchNames(con);
}
}
private ObservableList<String> fetchNames(Connection con) throws SQLException {
logger.info("Fetching names from database");
ObservableList<String> names = FXCollections.observableArrayList();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select name from employee");
while (rs.next()) {
names.add(rs.getString("name"));
}
logger.info("Found " + names.size() + " names");
return names;
}
}
// loads a collection of names fetched from a database into a listview.
// displays a progress indicator and disables the trigge button for
// the operation while the data is being fetched.
private void fetchNamesFromDatabaseToListView(
final Button triggerButton,
final ProgressIndicator databaseActivityIndicator,
final ListView listView) {
final FetchNamesTask fetchNamesTask = new FetchNamesTask();
triggerButton.setDisable(true);
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue());
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
triggerButton.setDisable(false);
databaseActivityIndicator.setVisible(false);
}
};
});
databaseExecutor.submit(fetchNamesTask);
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
logger.info("Getting a database connection");
Class.forName("org.h2.Driver");
return DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
}
abstract class DBTask<T> extends Task<T> {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
logger.log(Level.SEVERE, null, getException());
}
});
}
}
// executes database operations concurrent to JavaFX operations.
private ExecutorService databaseExecutor = Executors.newFixedThreadPool(
1,
new DatabaseThreadFactory()
);
static class DatabaseThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
#Override public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "Database-Connection-" + poolNumber.getAndIncrement() + "-thread");
thread.setDaemon(true);
return thread;
}
}
Note that once you start doing things concurrently, your coding and your UI gets more complicated than the default mode without Tasks when everything is single threaded. For example, in my sample I disabled the button which initiates the Task so you cannot have multiple Tasks running in the background doing the same thing (this kind of processing is similar to the web world where you might disable a form post button to prevent a form being double posted). I also added an animated progress indicator to the scene while the long running database task was executing so that the user has an indication that something is going on.
Sample program output demonstrating the UI experience when a long running database operation is in progress (note the progress indicator is animating during the fetch which means the UI is responsive though the screenshot does not show this):
To compare the additional complexity and functionality of an implementation with concurrent tasks versus an implementation which executes everything on the JavaFX application thread, you can see another version of the same sample which does not use tasks. Note that in my case with a toy, local database the additional complexity of the task based application is unnecessary because the local database operations execute so quickly, but if you were connecting to a large remote database using long running complex queries, than the Task based approach is worthwhile as it provides users with a smoother UI experience.
Managed to resolve using the solution provided by jewelsea. It is worth noting that if implementing this method when not using lists, tables and/or observable lists where you need to update an item on the UI such as a text field or label then simply add the update code within Platform.runLater. Below are some code snippets that show my working solution.
Code:
public void getSalesData() {
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Order_Information";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
todayTot = Double.parseDouble(rs.getString(7));
weekTot = Double.parseDouble(rs.getString(8));
monthTot = Double.parseDouble(rs.getString(9));
yearTot = Double.parseDouble(rs.getString(10));
yearTar = Double.parseDouble(rs.getString(11));
monthTar = Double.parseDouble(rs.getString(12));
weekTar = Double.parseDouble(rs.getString(13));
todayTar = Double.parseDouble(rs.getString(14));
deltaValue = Double.parseDouble(rs.getString(17));
yearPer = yearTot / yearTar * 100;
monthPer = monthTot / monthTar * 100;
weekPer = weekTot / weekTar * 100;
todayPer = todayTot / todayTar * 100;
//Doesn't update UI unless you add the update code to Platform.runLater...
Platform.runLater(new Runnable() {
public void run() {
todayTotal.setText("£" + formatter.format(todayTot));
weekTotal.setText("£" + formatter.format(weekTot));
monthTotal.setText("£" + formatter.format(monthTot));
yearTotal.setText("£" + formatter.format(yearTot));
yearTarget.setText("£" + formatter.format(yearTar));
monthTarget.setText("£" + formatter.format(monthTar));
weekTarget.setText("£" + formatter.format(weekTar));
todayTarget.setText("£" + formatter.format(todayTar));
yearPercent.setText(percentFormatter.format(yearPer) + "%");
currentDelta.setText("Current Delta (Week Ends): £"
+ formatter.format(deltaValue));
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
}
public void databaseThreadTester() {
fetchDataFromDB();
}
private void fetchDataFromDB() {
final testController.FetchNamesTask fetchNamesTask = new testController.FetchNamesTask();
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
databaseActivityIndicator.setVisible(false);
}
}
;
});
databaseExecutor.submit(fetchNamesTask);
}
abstract class DBTask<T> extends Task {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
}
}
class FetchNamesTask extends testController.DBTask {
#Override
protected String call() throws Exception {
fetchNames();
return null;
}
private void fetchNames() throws SQLException, InterruptedException {
Thread.sleep(5000);
getTopOrders();
getSalesData();
}
}
The only thing that doesn't appear to work with this implementation is the following, not sure why it doesn't work but it doesn't draw the graph.
public void addCricketGraphData() {
yearChart.getData().clear();
series.getData().clear();
series2.getData().clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "...omitted...";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
series.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(7))));
series2.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(8))));
} catch (SQLException ex) {
Logger.getLogger(testController.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
yearChart = createChart();
}
protected LineChart<String, Number> createChart() {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// setup chart
series.setName("Target");
series2.setName("Actual");
xAxis.setLabel("Period");
yAxis.setLabel("£");
//Add custom node for each point of data on the line chart.
for (int i = 0; i < series2.getData().size(); i++) {
nodeCounter = i;
final int value = series.getData().get(nodeCounter).getYValue().intValue();
final int value2 = series2.getData().get(nodeCounter).getYValue().intValue();
int result = value2 - value;
Node node = new HoveredThresholdNode(0, result);
node.toBack();
series2.getData().get(nodeCounter).setNode(node);
}
yearChart.getData().add(series);
yearChart.getData().add(series2);
return yearChart;
}
I need some help regarding my program. How do I pass data from JList into database?
In ItemDetails.java, I have passed the checkbox value into a JList in another JForm. Now I want to retrieve the data from JList into my database.
ItemDetails.java
private void jButtonNextActionPerformed(java.awt.event.ActionEvent evt) {
ShoppingCart sc=new ShoppingCart();
if(!jCheckBoxAdele.isSelected()&&!jCheckBox1DPerfect.isSelected()&&!jCheckBoxBieber.isSelected()
&&!jCheckBox1D1Thing.isSelected()&&!jCheckBoxHujan.isSelected()&&!jCheckBoxSamWriting.isSelected()
&&!jCheckBoxAlessiaWil.isSelected()&&!jCheckBoxSamStayWithMe.isSelected()
&&!jCheckBoxFx.isSelected()&&!jCheckBoxSonaone.isSelected()&&!jCheckBoxRabbani.isSelected()
&&!jCheckBoxTroye.isSelected()&&!jCheckBoxMarvinGaye.isSelected())
{
sc.setVisible(false);
JOptionPane.showMessageDialog(null, "Please pick your song !!!");
dispose();
new ItemDetails().setVisible(true);
}
if(jCheckBoxAdele.isSelected())
songsdetails.add("Adele - Hello");
if(jCheckBox1DPerfect.isSelected())
songsdetails.add("One Direction - Perfect");
if(jCheckBoxBieber.isSelected())
songsdetails.add("Justin Bieber - Sorry");
if(jCheckBox1D1Thing.isSelected())
songsdetails.add("One Direction - One Thing");
if(jCheckBoxHujan.isSelected())
songsdetails.add("Hujan - Anging Kencang");
if(jCheckBoxSamWriting.isSelected())
songsdetails.add("Sam Smith - Writing On The Walls");
if(jCheckBoxAlessiaWil.isSelected())
songsdetails.add("Alessia Cara");
if(jCheckBoxSamStayWithMe.isSelected())
songsdetails.add("Sam Smith - Stay With Me");
if(jCheckBoxFx.isSelected())
songsdetails.add("F(x) - 4 Walls");
if(jCheckBoxDemi.isSelected())
songsdetails.add("Demi Lovato - I Really Don't Care");
if(jCheckBoxSonaone.isSelected())
songsdetails.add("Sonaone - Firefly");
if(jCheckBoxRabbani.isSelected())
songsdetails.add("Rabbani - Pergi Tak Kembali");
if(jCheckBoxTroye.isSelected())
songsdetails.add("Troye Sivan - Fools");
if(jCheckBoxMarvinGaye.isSelected())
songsdetails.add("Charlie Puth - Marvin Gaye");
new ShoppingCart(songsdetails).setVisible(true);
dispose();
}
ShoppingCart.java
public class ShoppingCart extends javax.swing.JFrame {
public Connection cn;
public PreparedStatement st;
static ArrayList songsdetails;
/**
* Creates new form ShoppingCart
*/
public ShoppingCart() {
initComponents();
}
public ShoppingCart(ArrayList SONGSDETAILS) {
initComponents();
songsdetails = SONGSDETAILS;
}
private void jButtonEditActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false);
jList1.clearSelection();
new ItemDetails().setVisible(true);
}
private void jButtonNextActionPerformed(java.awt.event.ActionEvent evt) {
try {
Class.forName("com.mysql.jdbc.Driver");
cn=DriverManager.getConnection("jdbc:mysql://localhost:3306/sdmusic","root","");
st=cn.prepareStatement("SELECT `Username`, `SongsSelection` FROM `user` WHERE `Username`=? 'SongsSelection' = ?");
} catch (ClassNotFoundException ex) {
Logger.getLogger(ShoppingCart.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(ShoppingCart.class.getName()).log(Level.SEVERE, null, ex);
}
dispose();
new CalculatePay().setVisible(true);
}
private void formWindowOpened(java.awt.event.WindowEvent evt) {
// TODO add your handling code here:
DefaultListModel list = new DefaultListModel();
System.out.println(""+songsdetails.get(0));
for(int i=0; i <songsdetails.size();i++){
list.addElement(songsdetails.get(i));
}
jList1.setModel(list);
}
Retrieve needed values from your JList, see here
Prepare an INSERT INTO statement, see here
Execute your statement, see here
I'm wondering if anybody can help me with a rather annoying problem regarding creating a background thread in JavaFX! I currently have several SQL queries that add data to the UI which currently run on the JavaFX Application Thread (see example below). However when each of these queries execute it freezes the UI because it isn't running on a background thread. I've looked at various examples that use Task and sort of understand them but I cannot get them to work when doing database queries, some of which take a few seconds to run.
Here is one of the methods that executes a query:
public void getTopOrders() {
customerOrders.clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Get_Top_5_Customers_week";
ResultSet rs;
try (Statement stmt = con.createStatement();) {
rs = stmt.executeQuery(SQL);
while (rs.next()) {
double orderValue = Double.parseDouble(rs.getString(3));
customerOrders.add(new CustomerOrders(rs.getString(1),
rs.getString(2), "£" + formatter.format(orderValue),
rs.getString(4).substring(6, 8) + "/" +
rs.getString(4).substring(4, 6) + "/" +
rs.getString(4).substring(0, 4)));
}
}
} catch (SQLException | NumberFormatException e) {
}
}
Each processed record is added to an ObservableList which is linked to a TableView, or graph or simply sets the text on a label (depends on the query). How can I execute the query on a background thread and still leave the interface free to use and be updated from the queries
Thanks in advance
I created a sample solution for using a Task (as suggested in Alexander Kirov's comment) to access a database on a concurrently executing thread to the JavaFX application thread.
The relevant parts of the sample solution are reproduced below:
// fetches a collection of names from a database.
class FetchNamesTask extends DBTask<ObservableList<String>> {
#Override protected ObservableList<String> call() throws Exception {
// artificially pause for a while to simulate a long
// running database connection.
Thread.sleep(1000);
try (Connection con = getConnection()) {
return fetchNames(con);
}
}
private ObservableList<String> fetchNames(Connection con) throws SQLException {
logger.info("Fetching names from database");
ObservableList<String> names = FXCollections.observableArrayList();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select name from employee");
while (rs.next()) {
names.add(rs.getString("name"));
}
logger.info("Found " + names.size() + " names");
return names;
}
}
// loads a collection of names fetched from a database into a listview.
// displays a progress indicator and disables the trigge button for
// the operation while the data is being fetched.
private void fetchNamesFromDatabaseToListView(
final Button triggerButton,
final ProgressIndicator databaseActivityIndicator,
final ListView listView) {
final FetchNamesTask fetchNamesTask = new FetchNamesTask();
triggerButton.setDisable(true);
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue());
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
triggerButton.setDisable(false);
databaseActivityIndicator.setVisible(false);
}
};
});
databaseExecutor.submit(fetchNamesTask);
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
logger.info("Getting a database connection");
Class.forName("org.h2.Driver");
return DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
}
abstract class DBTask<T> extends Task<T> {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
logger.log(Level.SEVERE, null, getException());
}
});
}
}
// executes database operations concurrent to JavaFX operations.
private ExecutorService databaseExecutor = Executors.newFixedThreadPool(
1,
new DatabaseThreadFactory()
);
static class DatabaseThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
#Override public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "Database-Connection-" + poolNumber.getAndIncrement() + "-thread");
thread.setDaemon(true);
return thread;
}
}
Note that once you start doing things concurrently, your coding and your UI gets more complicated than the default mode without Tasks when everything is single threaded. For example, in my sample I disabled the button which initiates the Task so you cannot have multiple Tasks running in the background doing the same thing (this kind of processing is similar to the web world where you might disable a form post button to prevent a form being double posted). I also added an animated progress indicator to the scene while the long running database task was executing so that the user has an indication that something is going on.
Sample program output demonstrating the UI experience when a long running database operation is in progress (note the progress indicator is animating during the fetch which means the UI is responsive though the screenshot does not show this):
To compare the additional complexity and functionality of an implementation with concurrent tasks versus an implementation which executes everything on the JavaFX application thread, you can see another version of the same sample which does not use tasks. Note that in my case with a toy, local database the additional complexity of the task based application is unnecessary because the local database operations execute so quickly, but if you were connecting to a large remote database using long running complex queries, than the Task based approach is worthwhile as it provides users with a smoother UI experience.
Managed to resolve using the solution provided by jewelsea. It is worth noting that if implementing this method when not using lists, tables and/or observable lists where you need to update an item on the UI such as a text field or label then simply add the update code within Platform.runLater. Below are some code snippets that show my working solution.
Code:
public void getSalesData() {
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Order_Information";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
todayTot = Double.parseDouble(rs.getString(7));
weekTot = Double.parseDouble(rs.getString(8));
monthTot = Double.parseDouble(rs.getString(9));
yearTot = Double.parseDouble(rs.getString(10));
yearTar = Double.parseDouble(rs.getString(11));
monthTar = Double.parseDouble(rs.getString(12));
weekTar = Double.parseDouble(rs.getString(13));
todayTar = Double.parseDouble(rs.getString(14));
deltaValue = Double.parseDouble(rs.getString(17));
yearPer = yearTot / yearTar * 100;
monthPer = monthTot / monthTar * 100;
weekPer = weekTot / weekTar * 100;
todayPer = todayTot / todayTar * 100;
//Doesn't update UI unless you add the update code to Platform.runLater...
Platform.runLater(new Runnable() {
public void run() {
todayTotal.setText("£" + formatter.format(todayTot));
weekTotal.setText("£" + formatter.format(weekTot));
monthTotal.setText("£" + formatter.format(monthTot));
yearTotal.setText("£" + formatter.format(yearTot));
yearTarget.setText("£" + formatter.format(yearTar));
monthTarget.setText("£" + formatter.format(monthTar));
weekTarget.setText("£" + formatter.format(weekTar));
todayTarget.setText("£" + formatter.format(todayTar));
yearPercent.setText(percentFormatter.format(yearPer) + "%");
currentDelta.setText("Current Delta (Week Ends): £"
+ formatter.format(deltaValue));
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
}
public void databaseThreadTester() {
fetchDataFromDB();
}
private void fetchDataFromDB() {
final testController.FetchNamesTask fetchNamesTask = new testController.FetchNamesTask();
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
databaseActivityIndicator.setVisible(false);
}
}
;
});
databaseExecutor.submit(fetchNamesTask);
}
abstract class DBTask<T> extends Task {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
}
}
class FetchNamesTask extends testController.DBTask {
#Override
protected String call() throws Exception {
fetchNames();
return null;
}
private void fetchNames() throws SQLException, InterruptedException {
Thread.sleep(5000);
getTopOrders();
getSalesData();
}
}
The only thing that doesn't appear to work with this implementation is the following, not sure why it doesn't work but it doesn't draw the graph.
public void addCricketGraphData() {
yearChart.getData().clear();
series.getData().clear();
series2.getData().clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "...omitted...";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
series.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(7))));
series2.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(8))));
} catch (SQLException ex) {
Logger.getLogger(testController.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
yearChart = createChart();
}
protected LineChart<String, Number> createChart() {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// setup chart
series.setName("Target");
series2.setName("Actual");
xAxis.setLabel("Period");
yAxis.setLabel("£");
//Add custom node for each point of data on the line chart.
for (int i = 0; i < series2.getData().size(); i++) {
nodeCounter = i;
final int value = series.getData().get(nodeCounter).getYValue().intValue();
final int value2 = series2.getData().get(nodeCounter).getYValue().intValue();
int result = value2 - value;
Node node = new HoveredThresholdNode(0, result);
node.toBack();
series2.getData().get(nodeCounter).setNode(node);
}
yearChart.getData().add(series);
yearChart.getData().add(series2);
return yearChart;
}
I'm new in Libgdx and I'm getting trouble on using a database on my game.
I searched for a tutorial on how to make SQLite work on both Android and Desktop applications using Libgdx but I didn't found a easy one.
The last time I used a database in Android, I created a class that extends from SQLiteOpenHelper.
Is there a simple way to do the same using Libgdx? Or at least, can anyone point me to a step-by-step tutorial or something similar?
EDIT
I forgot to say that I'm looking for something that let me manage versions like SQLiteOpenHelper. In other words, I want to recreate my database in Android on apk installation, when I change the version of my DB on code.
SOLUTION
Following #42n4 answer, I managed how to connect to SQLite Database using SQLiteOpenHelper on Android Application and JDBC on Desktop Application.
First, I created a "common class" for both Desktop and Android Applications:
//General class that needs to be implemented on Android and Desktop Applications
public abstract class DataBase {
protected static String database_name="recycling_separation";
protected static DataBase instance = null;
protected static int version=1;
//Runs a sql query like "create".
public abstract void execute(String sql);
//Identical to execute but returns the number of rows affected (useful for updates)
public abstract int executeUpdate(String sql);
//Runs a query and returns an Object with all the results of the query. [Result Interface is defined below]
public abstract Result query(String sql);
public void onCreate(){
//Example of Highscore table code (You should change this for your own DB code creation)
execute("CREATE TABLE 'highscores' ('_id' INTEGER PRIMARY KEY NOT NULL , 'name' VARCHAR NOT NULL , 'score' INTEGER NOT NULL );");
execute("INSERT INTO 'highscores'(name,score) values ('Cris',1234)");
//Example of query to get DB data of Highscore table
Result q=query("SELECT * FROM 'highscores'");
if (!q.isEmpty()){
q.moveToNext();
System.out.println("Highscore of "+q.getString(q.getColumnIndex("name"))+": "+q.getString(q.getColumnIndex("score")));
}
}
public void onUpgrade(){
//Example code (You should change this for your own DB code)
execute("DROP TABLE IF EXISTS 'highscores';");
onCreate();
System.out.println("DB Upgrade maded because I changed DataBase.version on code");
}
//Interface to be implemented on both Android and Desktop Applications
public interface Result{
public boolean isEmpty();
public boolean moveToNext();
public int getColumnIndex(String name);
public float getFloat(int columnIndex);
[...]
}
}
Then, I created a DatabaseDesktop Class for Desktop Application:
public class DatabaseDesktop extends DataBase{
protected Connection db_connection;
protected Statement stmt;
protected boolean nodatabase=false;
public DatabaseDesktop() {
loadDatabase();
if (isNewDatabase()){
onCreate();
upgradeVersion();
} else if (isVersionDifferent()){
onUpgrade();
upgradeVersion();
}
}
public void execute(String sql){
try {
stmt.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
public int executeUpdate(String sql){
try {
return stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
public Result query(String sql) {
try {
return new ResultDesktop(stmt.executeQuery(sql));
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private void loadDatabase(){
File file = new File (database_name+".db");
if(!file.exists())
nodatabase=true;
try {
Class.forName("org.sqlite.JDBC");
db_connection = DriverManager.getConnection("jdbc:sqlite:"+database_name+".db");
stmt = db_connection.createStatement();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void upgradeVersion() {
execute("PRAGMA user_version="+version);
}
private boolean isNewDatabase() {
return nodatabase;
}
private boolean isVersionDifferent(){
Result q=query("PRAGMA user_version");
if (!q.isEmpty())
return (q.getInt(1)!=version);
else
return true;
}
public class ResultDesktop implements Result{
ResultSet res;
boolean called_is_empty=false;
public ResultDesktop(ResultSet res) {
this.res = res;
}
public boolean isEmpty() {
try {
if (res.getRow()==0){
called_is_empty=true;
return !res.next();
}
return res.getRow()==0;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public boolean moveToNext() {
try {
if (called_is_empty){
called_is_empty=false;
return true;
} else
return res.next();
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public int getColumnIndex(String name) {
try {
return res.findColumn(name);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
public float getFloat(int columnIndex) {
try {
return res.getFloat(columnIndex);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
[...]
}
}
And a DatabaseAndroid for Android Application
public class DatabaseAndroid extends DataBase{
protected SQLiteOpenHelper db_connection;
protected SQLiteDatabase stmt;
public DatabaseAndroid(Context context) {
db_connection = new AndroidDB(context, database_name, null, version);
stmt=db_connection.getWritableDatabase();
}
public void execute(String sql){
stmt.execSQL(sql);
}
public int executeUpdate(String sql){
stmt.execSQL(sql);
SQLiteStatement tmp = stmt.compileStatement("SELECT CHANGES()");
return (int) tmp.simpleQueryForLong();
}
public Result query(String sql) {
ResultAndroid result=new ResultAndroid(stmt.rawQuery(sql,null));
return result;
}
class AndroidDB extends SQLiteOpenHelper {
public AndroidDB(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
public void onCreate(SQLiteDatabase db) {
stmt=db;
DatabaseAndroid.this.onCreate();
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
stmt=db;
DatabaseAndroid.this.onUpgrade();
}
}
public class ResultAndroid implements Result{
Cursor cursor;
public ResultAndroid(Cursor cursor) {
this.cursor=cursor;
}
public boolean isEmpty() {
return cursor.getCount()==0;
}
public int getColumnIndex(String name) {
return cursor.getColumnIndex(name);
}
public String[] getColumnNames() {
return cursor.getColumnNames();
}
public float getFloat(int columnIndex) {
return cursor.getFloat(columnIndex);
}
[...]
}
}
Finally, I changed the Main Classes of both Android and Desktop Applications:
public class Main extends AndroidApplication {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialize(new MyGame(new DatabaseAndroid(this.getBaseContext())), false);
}
}
public class Main {
public static void main(String[] args) {
new LwjglApplication(new MyGame(new DatabaseDesktop()), "Example", MyGame.SCREEN_WIDTH, MyGame.SCREEN_HEIGHT,false);
}
}
Note that:
I made a version management like the one that happens in SQLiteOpenHelper using the PRAGMA user_version. This way, I just change the version of the DataBase class when I need to upgrade it.
I didn't put all the methods that I made on Result but, I put the ones that I think that are more important.that are more important.
There is an extension (called gdx-sqlite) that I wrote which will do most of the work you require. Latest build of this extension can be downloaded from here. The source code and read me are located at: https://github.com/mrafayaleem/gdx-sqlite
This extension currently supports Android and Desktop platforms. Also, there is no support to open databases located in the assets folder of the Android app. However, this is a pending feature and will be added soon.
Follow the instructions in read me to setup your projects for database handling. Following is an example code:
package com.mrafayaleem.gdxsqlitetest;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.sql.Database;
import com.badlogic.gdx.sql.DatabaseCursor;
import com.badlogic.gdx.sql.DatabaseFactory;
import com.badlogic.gdx.sql.SQLiteGdxException;
public class DatabaseTest {
Database dbHandler;
public static final String TABLE_COMMENTS = "comments";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_COMMENT = "comment";
private static final String DATABASE_NAME = "comments.db";
private static final int DATABASE_VERSION = 1;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table if not exists "
+ TABLE_COMMENTS + "(" + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_COMMENT
+ " text not null);";
public DatabaseTest() {
Gdx.app.log("DatabaseTest", "creation started");
dbHandler = DatabaseFactory.getNewDatabase(DATABASE_NAME,
DATABASE_VERSION, DATABASE_CREATE, null);
dbHandler.setupDatabase();
try {
dbHandler.openOrCreateDatabase();
dbHandler.execSQL(DATABASE_CREATE);
} catch (SQLiteGdxException e) {
e.printStackTrace();
}
Gdx.app.log("DatabaseTest", "created successfully");
try {
dbHandler
.execSQL("INSERT INTO comments ('comment') VALUES ('This is a test comment')");
} catch (SQLiteGdxException e) {
e.printStackTrace();
}
DatabaseCursor cursor = null;
try {
cursor = dbHandler.rawQuery("SELECT * FROM comments");
} catch (SQLiteGdxException e) {
e.printStackTrace();
}
while (cursor.next()) {
Gdx.app.log("FromDb", String.valueOf(cursor.getString(1)));
}
try {
dbHandler.closeDatabase();
} catch (SQLiteGdxException e) {
e.printStackTrace();
}
dbHandler = null;
Gdx.app.log("DatabaseTest", "dispose");
}
}
http://marakana.com/techtv/android_bootcamp_screencast_series.html
Class 4, Part 1: Android Bootcamp - statusData, for libgdx: http://code.google.com/p/libgdx-users/wiki/SQLite
EDIT:
I should mention about two new courses about libgdx games at Udacity:
https://github.com/udacity/ud405
https://github.com/udacity/ud406
I am having a run method which tries to override another run method. But its not happening because I am getting a "Class not found Exception" before it passed on to run method.
Here´s my class with run method
public class PollingSynchronizer implements Runnable{
public Collection<KamMessage> incomingQueue,outgoingQueue,fetchedMessages;
private Connection dbConnection;
/**
* Constructor. Requires to provide a reference to the Kam message queue
*
* #param incomingMessages reference to message queue
* #param dbConnection
*
*/
public PollingSynchronizer(Collection<KpiMessage> incomingQueue, Connection dbConnection) {
super();
this.incomingQueue = incomingQueue;
this.dbConnection = dbConnection;
}
private int seqId;
public int getSeqId() {
return seqId;
}
public void setSeqId(int seqId) {
this.seqId = seqId;
}
#Override
/**
* The method which runs Polling action and record the time at which it is done
*
*/
public void run() {
int seqId = 0;
while(true) {
List<KamMessage> list = null;
try {
list = fullPoll(seqId);
if (!list.isEmpty()) {
seqId = list.get(0).getSequence();
incomingQueue.addAll(list);
this.outgoingQueue = incomingQueue;
System.out.println("waiting 3 seconds");
System.out.println("new incoming message");
Thread.sleep(3000);
//when I debug my execution stops here and throws exception
MessageProcessor processor = new MessageProcessor() {
#Override
public void run() {
new MessageProcessor().generate(outgoingQueue);
}
};
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
This is the method which I have to call in order to execute.
public abstract class MessageProcessor implements Runnable {
private Collection<KpiMessage> fetchedMessages;
private Connection dbConnection;
Statement st = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
private Collection<KpiMessage> outgoingQueue;
public KpiMsg804 MessageProcessor(Collection<KpiMessage> outgoingQueue, Connection
dbConnection){
this.outgoingQueue = outgoingQueue;
this.dbConnection = dbConnection;
return (KpiMsg804) fetchedMessages;
}
public Collection<KamMessage> generate(Collection<KamMessage> outgoingQueue)
{
while(true){
try {
while (rs.next()) {
KamMessage filedClass = convertRecordsetToPojo(rs);
outgoingQueue.add(filedClass);
}
for (KamMessage pojoClass : outgoingQueue) {
KamMsg804 updatedValue = createKamMsg804(pojoClass);
System.out.print(" " + pojoClass.getSequence());
System.out.print(" " + pojoClass.getTableName());
System.out.print(" " + pojoClass.getAction());
System.out.print(" " + updatedValue.getKeyInfo1());
System.out.print(" " + updatedValue.getKeyInfo2());
System.out.println(" " + pojoClass.getEntryTime());
}
return outgoingQueue;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
How can I implement this?
Since I am new here, please give a reason for thumbs down. So that I can explain my question.
Try this:
MessageProcessor processor = new MessageProcessor() {
#Override
public void run() {
**new MessageProcessor()**.generate(outgoingQueue);
}
};
MessageProcessor is an abstract class. object creation inside the run method should have failed at compile time.
processor object is created but unused.. you need to create a thread with the processor instace and start the thread.