5

I'm newbie Java programmer. I would like to insert ResultSet data to JavaFX TableView, but i wouldn't use intermediate class. Can i insert ResultSet rows to TableView rows as objects?

Here is my code with using intermediate class Unit

 public static ObservableList<Unit> getUnits() {

    final ObservableList<Unit> data = FXCollections.observableArrayList();
    if (openConnection()) {
        try {
            rs = st.executeQuery("SELECT * FROM units");
            while (rs.next()) {

                data.add(new Unit(rs.getString("id_unit"),
                        rs.getString("short_name"),
                        rs.getString("name")));
            }
        } catch (Exception ex) {
            Logger.getLogger(SQLConnect.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    closeConnection();
    return data;
}

Controller

idUnit.setCellValueFactory(new PropertyValueFactory<Unit, String>("idUnit"));
shortNameUnit.setCellValueFactory(new PropertyValueFactory<Unit, String>("shortName"));
nameUnit.setCellValueFactory(new PropertyValueFactory<Unit, String>("name"));

unitsTableView.setItems(SQLConnect.getUnits());

2 Answers 2

4

You need something to hold the data; that something (which you call the "intermediate class") is going to be the data type for your TableView.

It doesn't necessarily have to be a class you create, but if you just use something general, your code is going to be much harder to understand. For example, you could just use a List<String> to hold each row:

TableView<List<String>> unitsTableView = new TableView<>();
idUnit = new TableColumn<List<String>, String>("Id");
idUnit.setCellValueFactory(new Callback<CellDataFeatures<List<String>, String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(CellDataFeatures<List<String>, String>> data) {
        return new ReadOnlyStringWrapper(data.getValue().get(0)) ;
    }
});
shortNameUnit = new TableColumn<List<String>, String>("Short Name");
shortNameUnit.setCellValueFactory(new Callback<CellDataFeatures<List<String>, String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(CellDataFeatures<List<String>, String>> data) {
        return new ReadOnlyStringWrapper(data.getValue().get(1)) ;
    }
});
nameUnit = new TableColumn<List<String>, String>("Name");
nameUnit.setCellValueFactory(new Callback<CellDataFeatures<List<String>, String>, ObservableValue<String>>() {
    @Override
    public ObservableValue<String> call(CellDataFeatures<List<String>, String>> data) {
        return new ReadOnlyStringWrapper(data.getValue().get(2)) ;
    }
});

and then

public static ObservableList<List<String>> getUnits() {

    final ObservableList<List<String>> data = FXCollections.observableArrayList();
    if (openConnection()) {
        try {
            rs = st.executeQuery("SELECT * FROM units");
            while (rs.next()) {
                List<String> unit = new ArrayList<>();
                unit.add(rs.getString("id_unit"));
                unit.add(rs.getString("short_name"));
                unit.add(rs.getString("name"));
                data.add(unit);
            }
        } catch (Exception ex) {
            Logger.getLogger(SQLConnect.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    closeConnection();
    return data;
}

But now your code is almost completely devoid of the semantics of the data you are representing.

Note that you can't use a ResultSet directly, for a number of reasons. One is that the data in the TableView are represented by a random-access list. While you can think of ResultSet as representing a list of rows of data, it doesn't implement the List interface, and doesn't have any random-access functionality (there's no guaranteed way to get row number 5, etc.). Another reason is that a ResultSet requires (or at least may require, depending on the JDBC driver implementation) a live database resource. So you would risk locking your database table for the entire time the TableView is in scope.

Why do you want to avoid creating the data representation class?

Sign up to request clarification or add additional context in comments.

4 Comments

This has moderate performance implications. You're creating a new ReadOnlyStringWrapper every time the cell value requested. For example, when sorting on a row on my ~20000 entry table, the Cell Value Factory is called about 450.000 times. Unfortunately there doesn't seem to be a way to just pass a String to the Cell Value Factory.
@Marv Correct. As I suggest at the bottom of the post, it's better to create a data model class and follow the expected pattern.
I agree. Unfortunately, this forces you to have Properties even for immutable or derived values, which kind of forces you into unnecessary overhead. For example, wanting to display the first item of a list of strings inside a model class becomes a bit of a headache unless it's a list of ObservableStringValues. I guess properties are relatively lightweight, but still.
@Marv Well, all you really need is an ObservableValue, not a Property; so you could provide a very minimal implementation yourself if you need to optimize. It's still some overhead compared to a plain value, but barely any (e.g. for an immutable value your add/remove listeners could be no-ops and you just implement getValue() to return the value).
1

A workaround could be to create your custom component that extends TableView and then do the magic inside your own CustomTableView class

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.