我正在尝试使用JavaFX 8和休眠中的TableView映射和显示表之间的数据,表之间存在一对一关系。

表格:

@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
}


@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
}

带有ClubControllerTableView类:
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;
    }

    ...
}

前两列可以正常工作,但是在trainerColumn中,我有NPE。如何解决?

最佳答案

trainer实体中的CLUB字段是延迟加载的:

...
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "club", cascade = CascadeType.ALL)
    private Trainer trainer;
...

...只能在打开创建实体的会话时读取。

ClubController中,您打开和关闭了会话,而没有从数据库中明确加载trainer详细信息:
    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;
    }

因此,要么使trainer实体中的CLUB字段都渴望加载:
...
    @OneToOne(fetch = FetchType.EAGER, mappedBy = "club", cascade = CascadeType.ALL)
    private Trainer trainer;
...

或在查询数据库时显式加载trainer,方法很简单:
...
        // 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();
...

尽管我建议不要使用最后一个,因为它有点脏且性能较差(许多额外的往返数据库操作,
如果需要,则不是针对所有一对一记录查询的单个联接查询);但仍然可用。

09-12 17:32