Статьи

NetBeans в классе: JavaFX и JDBC — выигрышная комбинация


колледже Доусон в Монреале, Канада. Он также является программным консультантом и по совместительству преподавателем в Школе расширенного обучения при Институте вычислительной техники Университета Конкордия . Он ведет блог на omniprogrammer.com и пишет в Твиттере @omniprof . Его регулярные колонки о NetBeans в образовании перечислены здесь .

В этой статье я покажу вам, как я учу своих студентов программировать JDBC для настольных приложений JavaFX. Мой подход работает следующим образом:

  1. Используйте JavaFX bean-компоненты
  2. Установите соединение, чтобы войти в базу данных
  3. Оформите запрос через подготовленное заявление
  4. Скопируйте результаты в подходящую структуру данных
  5. Закройте все объекты и затем выйдите из базы данных

Этот тип подхода необходим, когда настольное приложение использует сервер базы данных, совместно используемый другими приложениями или пользователями. Подход, использующий пул соединений, является предпочтительным способом работы с базами данных в приложении на стороне сервера и может также подойти в некоторых случаях на настольном компьютере.

Давайте начнем с компонента данных. При создании приложения JavaFX вы должны написать свои bean-компоненты, чтобы поля могли связываться с элементом управления. В то же время вы хотите, чтобы компонент продолжал использоваться в классах, которые не знают о JavaFX.

Начните с создания всех полей как свойств JavaFX. Показанные свойства являются интерфейсами, которые будут созданы соответствующим классом в конструкторе.

public class FishData {

    private IntegerProperty id;
    private StringProperty commonName;
    private StringProperty latin;
    private StringProperty ph;
    private StringProperty kh;
    private StringProperty temp;
    private StringProperty fishSize;
    private StringProperty speciesOrigin;
    private StringProperty tankSize;
    private StringProperty stocking;
    private StringProperty diet;

Сигнатура конструктора не по умолчанию — это сигнатура обычного компонента данных. Аргументы — это все примитивы или фактические типы объектов, которые будет содержать компонент. Код конструктора создает экземпляр объекта свойства и передает ему соответствующий аргумент. Конструктор по умолчанию просто вызывает конструктор не по умолчанию с начальными значениями, которые вы определили как соответствующие.

    public FishData(int id, String commonName, String latin, String ph,
            String kh, String temp, String fishSize, String speciesOrigin,
            String tankSize, String stocking, String diet) {
        super();
        this.id = new SimpleIntegerProperty(id);
        this.commonName = new SimpleStringProperty(commonName);
        this.latin = new SimpleStringProperty(latin);
        this.ph = new SimpleStringProperty(ph);
        this.kh = new SimpleStringProperty(kh);
        this.temp = new SimpleStringProperty(temp);
        this.fishSize = new SimpleStringProperty(fishSize);
        this.speciesOrigin = new SimpleStringProperty(speciesOrigin);
        this.tankSize = new SimpleStringProperty(tankSize);
        this.stocking = new SimpleStringProperty(stocking);
        this.diet = new SimpleStringProperty(diet);
    }

    public FishData() {
        this(-1, "", "", "", "", "", "", "", "", "", "");
    }

Если ваши поля содержат объект, который не имеет соответствующего свойства, такого как BigDecimal, вы можете использовать ObjectProperty.

private ObjectProperty unitCost;

В конструкторе это будет выглядеть так:

this.unitCost = new SimpleObjectProperty<>(unitCost);

Установщики и получатели в bean-компоненте JavaFX имеют тот же интерфейс, что и обычный bean-компонент. Это то, что обеспечивает совместимость. Внутри этих методов код либо извлекает значение из объекта свойства, либо устанавливает значение в объекте свойства. Вы никогда не используете set или get для прикосновения к реальному объекту свойства.

    public int getId() {
        return id.get(); // Use get on the propert
    }

    public void setId(int id) {
        this.id.set(id); // Use set on the property
    }

Для компонентов JavaFX требуется третий метод для каждого поля, которое вы привязываете к элементу управления. Этот метод возвращает объект свойства. Этот метод должен использоваться только в привязке. Я не уверен, существует ли соглашение об именах, но я говорю своим ученикам использовать имя поля плюс слово «Свойство».

    public IntegerProperty idProperty() {
        return id;
    }

Вот метод, который будет извлекать все записи из таблицы, содержащей записи рыбы. Переменные url, user и password были объявлены, и я учу своих студентов, что эта информация должна быть загружена из файла свойств.

public ObservableList findAll() throws SQLException {

   ObservableList fishData = FXCollections.observableArrayList();

   String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH";

   // Using try with resources, available since Java 1.7
   // Classes that implement the Closable interface created in the
   // parenthesis () will be closed when the block ends.
   try (Connection connection = DriverManager.getConnection(url, user, password);
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);
        ResultSet resultSet = pStatement.executeQuery()) {

      while (resultSet.next()) {
         fishData.add(createFishData(resultSet));
      }
   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }
   log.info("# of records found : " + fishData.size());
   return fishData;
}

public ObservableList findAll() throws SQLException {

Метод возвращает ObservableList объектов FishData. ObservableList реализует интерфейсы Collection и List, что делает его совместимым, например, с ArrayList. Существующий код, который ожидает ArrayList, продолжит работать с этим методом.

   try (Connection connection = DriverManager.getConnection(url, user, password);
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);
        ResultSet resultSet = pStatement.executeQuery()) {

Эта попытка использует попытку с ресурсами, представленную в Java 7. Каждый из объектов в скобках реализует интерфейс Closable. Это означает, что когда этот блок try завершается успешно или из-за исключения, все объекты будут закрыты в обратном порядке после их создания.

      while (resultSet.next()) {
         fishData.add(createFishData(resultSet));
      }

Здесь код проходит через ResultSet, вызывает закрытый метод для создания объектов FishData, а затем добавляет объекты в ObservbleList. Вот частный метод.

    private FishData createFishData(ResultSet resultSet) throws SQLException {
        FishData fishData = new FishData();
        fishData.setCommonName(resultSet.getString("COMMONNAME"));
        fishData.setDiet(resultSet.getString("DIET"));
        fishData.setKh(resultSet.getString("KH"));
        fishData.setLatin(resultSet.getString("LATIN"));
        fishData.setPh(resultSet.getString("PH"));
        fishData.setFishSize(resultSet.getString("FISHSIZE"));
        fishData.setSpeciesOrigin(resultSet.getString("SPECIESORIGIN"));
        fishData.setStocking(resultSet.getString("STOCKING"));
        fishData.setTankSize(resultSet.getString("TANKSIZE"));
        fishData.setTemp(resultSet.getString("TEMP"));
        fishData.setId(resultSet.getInt("ID"));
        return fishData;
    }

Назад в findAll это блок catch.

   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }

Раньше я учил своих учеников просто использовать условие throws и не пытаться поймать исключение здесь. Недавнее чтение на эту тему, а также некоторые занятия в классе изменили мой подход. Исключение теперь перехватывается и регистрируется перед повторным вызовом. Я не верю, что менеджер баз данных должен принимать решение о том, что делать с исключением, поэтому я перебрасываю его.

Последний пример — это метод, использующий в SQL предложение «where». Вот когда PreparedStatement является обязательным. Для этого также требуется внутренняя попытка с ресурсами, поскольку в скобках попытки может быть размещено только создание экземпляров объектов.

public FishData findID(int id) throws SQLException {

   // If there is no record with the desired id then this will be returned
   // as a null pointer
   FishData fishData = null;

   String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH WHERE ID = ?";

   // Using try with resources, available since Java 1.7
   // Classes that implement the Closable interface created in the
   // parenthesis () will be closed when the block ends.
   try (Connection connection = DriverManager.getConnection(url, user, password);

        // You must use PreparedStatements to guard against SQL Injection
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);) {

      // Only object creation statements can be in the parenthesis so
      // first try-with-resources block ends
 
     pStatement.setInt(1, id);

      // A new try-with-resources block for creating the ResultSet object begins
      try (ResultSet resultSet = pStatement.executeQuery()) {

         if (resultSet.next()) {
            fishData = createFishData(resultSet);
         }
      }
   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }
   log.info("Found " + id + "?: " + (fishData != null));
   return fishData;
}

Я надеюсь, что это сделает вашу кодировку JDBC более надежной и что вы перейдете на JavaFX для своих графических интерфейсов.