колледже Доусон в Монреале, Канада. Он также является программным консультантом и по совместительству преподавателем в Школе расширенного обучения при Институте вычислительной техники Университета Конкордия . Он ведет блог на omniprogrammer.com и пишет в Твиттере @omniprof . Его регулярные колонки о NetBeans в образовании перечислены здесь .
В этой статье я покажу вам, как я учу своих студентов программировать JDBC для настольных приложений JavaFX. Мой подход работает следующим образом:
- Используйте JavaFX bean-компоненты
- Установите соединение, чтобы войти в базу данных
- Оформите запрос через подготовленное заявление
- Скопируйте результаты в подходящую структуру данных
- Закройте все объекты и затем выйдите из базы данных
Этот тип подхода необходим, когда настольное приложение использует сервер базы данных, совместно используемый другими приложениями или пользователями. Подход, использующий пул соединений, является предпочтительным способом работы с базами данных в приложении на стороне сервера и может также подойти в некоторых случаях на настольном компьютере.
Давайте начнем с компонента данных. При создании приложения 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 для своих графических интерфейсов.