Issue
I'm trying to generate statistical graphs (BarCharts and LineCharts) from the data saved in an sql database using Java. The Data is expected to change from time to time.
I have tried using ObservableLists but am getting error messages.
Below is what I have tried so far; NOTE: This resides in the DatabaseHandler class
public ObservableList<BarChart.Data> getItemGraphStatistics(){
ObservableList<BarChart.Data> data = FXCollections.observableArrayList();
String sql = "SELECT itemID, COUNT(*) FROM (SELECT itemID FROM SALES UNION ALL SELECT itemID FROM STOCK) t GROUP BY itemID";
System.out.println(sql );
ResultSet rs = handler.execQuery(sql);
try{
while(rs.next()){
String item= rs.getString("itemID");
int count = rs.getInt(2);
data.add(new BarChart.Data(item+ "(" + count + ")", count));
}
} catch (SQLException ex) {
Logger.getLogger(DatabaseHandler.class.getName()).log(Level.SEVERE, null, ex);
}
return data;
}
On running the above am getting
java.lang.ClassCastException: javafx.scene.chart.XYChart$Data cannot be cast to javafx.scene.chart.XYChart$Series
When i try to change BarChart.Data to BarChart.Series, I get unable to convert int to observableList error message.
Below is an example of the trial of the above;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.controls.JFXTextField;
import java.net.URL;
import java.util.Calendar;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.PieChart;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import sales.manager.database.DatabaseHandler;
/**
* FXML Controller class
*
* @author Garande
*/
public class PerformanceAnalyzerController implements Initializable {
@FXML
private AnchorPane analysisHandler;
@FXML
private AnchorPane graphHandler;
@FXML
private PieChart piechart;
@FXML
private BarChart<?, ?> barchart;
@FXML
private LineChart<?, ?> linechart;
@FXML
private NumberAxis yBarGraph;
@FXML
private CategoryAxis xBarGraph;
@FXML
private NumberAxis yLineGraph;
@FXML
private CategoryAxis xLineGraph;
DatabaseHandler handler = DatabaseHandler.getInstance();
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
}
@FXML
private void performAnalysisOperation(ActionEvent event) {
//Performing analysis operation
barchart.setData(handler.getItemGraphStatistics());
graphHandler.getChildren().add(barchart);
}
}
Solution
The data
property of an XYChart
holds an ObservableList<XYChart.Series>
. Each XYChart.Series
also has a data
property, but it holds an ObservableList<XYChart.Data>
. The problem is your method returns an ObservableList<XYChart.Data>
that you then try to set on XYChart.data
—the types don't match. I'm not sure how you're getting a ClassCastException
, as your code shouldn't even compile.
You need to wrap your list of data in a series, then add the series to the chart. You should also avoid using raw types. Both Series
and Data
have type parameters but you don't specify them. Based on the way you're building your Data
, your code should look something like (some code omitted for brevity):
public class PerformanceAnalyzerController {
@FXML private BarChart<String, Number> barChart;
@FXML
private void performAnalysisOperation(ActionEvent event) {
//Performing analysis operation
ObservableList<XYChart.Data<String, Number>> data = handler.getItemsGraphicsStatisitics();
XYChart.Series<String, Number> series = new XYChart.Series<>("Series Name", data);
barchart.getData().setAll(series);
graphHandler.getChildren().add(barchart);
}
}
And:
// Use generic types
public ObservableList<XYChart.Data<String, Number>> getItemGraphStatistics(){
ObservableList<XYChart.Data<String, Number>> data = FXCollections.observableArrayList();
String sql = "SELECT itemID, COUNT(*) FROM (SELECT itemID FROM SALES UNION ALL SELECT itemID FROM STOCK) t GROUP BY itemID";
System.out.println(sql );
ResultSet rs = handler.execQuery(sql);
try{
while(rs.next()){
String item= rs.getString("itemID");
int count = rs.getInt(2);
// add diamond operator "<>"
data.add(new XYChart.Data<>(item+ "(" + count + ")", count));
}
} catch (SQLException ex) {
Logger.getLogger(DatabaseHandler.class.getName()).log(Level.SEVERE, null, ex);
}
return data;
}
Note: I replaced uses of BarChart.Data
with XYChart.Data
, but I don't believe it matters in this case.
Answered By - Slaw