Issue
I'm trying to map and display data from tables between which there is a one-to-one relationship using TableView
from JavaFX 8 and hibernate.
Tables:
@Entity
@Table(name = "CLUB")
public class Club implements Serializable {
@Id
@Column(name = "CLUB_ID", nullable = false, unique = true)
private int clubId;
@Column(name = "NAME", nullable = false, length = 100)
private String name;
@Column(name = "CITY", length = 50)
private String city;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "club", cascade = CascadeType.ALL)
private Trainer trainer;
// constructor, getters, setters
}
and
@Entity
@Table(name = "TRAINER")
public class Trainer implements Serializable {
@Id
@Column(name = "LICENCE_NR", nullable = false, unique = true)
private int licenceNr;
@Column(name = "NAME", nullable = false, length = 50)
private String name;
@Column(name = "SURNAME", nullable = false, length = 50)
private String surname;
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private Club club;
// constructor, getters, setters
}
ClubController
class with TableView
:
public class ClubController implements Initializable {
...
@FXML
private TableView<Club> clubTableView;
@FXML
private TableColumn<Club, String> nameColumn;
@FXML
private TableColumn<Club, String> cityColumn;
@FXML
private TableColumn<Club, Integer> trainerColumn;
@Override
public void initialize(URL location, ResourceBundle resources) {
nameColumn.setCellValueFactory(new PropertyValueFactory<Club, String>("name"));
cityColumn.setCellValueFactory(new PropertyValueFactory<Club, String>("city"));
trainerColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Club, Integer>, ObservableValue<Integer>>() {
@Override
public ObservableValue<Integer> call(TableColumn.CellDataFeatures<Club, Integer> param) {
return new SimpleIntegerProperty(param.getValue().getTrainer().getLicenceNr()).asObject();
}
});
clubTableView.setItems(getClub());
}
private ObservableList<Club> getClub() {
ObservableList<Club> clubList = FXCollections.observableArrayList();
Session session = HibernateUtil.config().openSession();
List<Club> cList = session.createCriteria(Club.class).list();
clubList.addAll(cList);
session.close();
return clubList;
}
...
}
First two columns work correctly, but in the trainerColumn
I have NPE. How to fix it?
Solution
The trainer
field in the CLUB
entity is lazy loaded:
...
@OneToOne(fetch = FetchType.LAZY, mappedBy = "club", cascade = CascadeType.ALL)
private Trainer trainer;
...
... which can only be read while the session that created the entity is opened.
In the ClubController
, you opened and closed the session, without explicitly loading the trainer
details from the DB:
private ObservableList<Club> getClub() {
ObservableList<Club> clubList = FXCollections.observableArrayList();
// SESSION OPENED
Session session = HibernateUtil.config().openSession();
// CLUB ENTITIES LOADED WITH LAZY TRAINERS
List<Club> cList = session.createCriteria(Club.class).list();
clubList.addAll(cList);
//SESSION CLOSED
session.close();
return clubList;
}
So either make the trainer
field in the CLUB
entity to be eager loaded:
...
@OneToOne(fetch = FetchType.EAGER, mappedBy = "club", cascade = CascadeType.ALL)
private Trainer trainer;
...
Or explicitly load the trainer
when querying the DB - with something as simple as:
...
// SESSION OPENED
Session session = HibernateUtil.config().openSession();
// CLUB ENTITIES LOADED WITH LAZY TRAINERS
List<Club> cList = session.createCriteria(Club.class).list();
// EXPLICITLY LOAD ALL TRAINERS FROM DB
cList.stream().peek(club -> club.getTrainer().getLicenceNr())
clubList.addAll(cList);
//SESSION CLOSED
session.close();
...
Although, I advice against this last one, since is a bit dirty and less performing (many extra round trips to the DB, instead of a single join query for all one-to-one record query if eager); but still available.
Answered By - Marco R.